激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務(wù)器之家 - 編程語言 - Java教程 - Netty 基石,Java NIO 核心知識(shí)

Netty 基石,Java NIO 核心知識(shí)

2021-12-27 23:32yes的練級(jí)攻略是Yes呀 Java教程

在深入 Netty 之前,我覺得有必要先對齊一下 Java NIO 的基礎(chǔ)知識(shí),因?yàn)?Netty 對底層網(wǎng)絡(luò) I/O 的操作就是基于 Java NIO 的,所以有必要了解一下。

Netty 基石,Java NIO 核心知識(shí)

本文轉(zhuǎn)載自微信公眾號(hào)「yes的練級(jí)攻略」,作者是Yes呀 。轉(zhuǎn)載本文請聯(lián)系yes的練級(jí)攻略公眾號(hào)。

你好,我是yes。

在深入 Netty 之前,我覺得有必要先對齊一下 Java NIO 的基礎(chǔ)知識(shí),因?yàn)?Netty 對底層網(wǎng)絡(luò) I/O 的操作就是基于 Java NIO 的,所以有必要了解一下。

到時(shí)候看源碼,會(huì)有很多概念,例如 Channel、Selector、SelectionKey、Buffer 等等,這篇我們就來了解下這些名詞到底代表著什么,分別是什么意思。

關(guān)于 Java NIO 相關(guān)的核心,總的來看包含以下三點(diǎn),分別是:

  • Channel
  • Buffer
  • Selector

什么是 Channel

翻譯過來就是通道。

我們可以往通道里寫數(shù)據(jù),也可以從通道里讀數(shù)據(jù),它是雙向的,而與之配套的是 Buffer,也就是你想要往一個(gè)通道里寫數(shù)據(jù),必須要將數(shù)據(jù)寫到一個(gè) Buffer 中,然后寫到通道里。

從通道里讀數(shù)據(jù),必須將通道的數(shù)據(jù)先讀取到一個(gè) Buffer 中,然后再操作。

在 NIO 中 Channel 有多種類型:

  • SocketChannel
  • ServerSocketChannel
  • DatagramChannel
  • FileChannel
  • SocketChannel

對標(biāo) Socket,我們可以直接將它當(dāng)做所建立的連接。

通過 SocketChannel ,我們可以利用 TCP 協(xié)議進(jìn)行讀寫網(wǎng)絡(luò)數(shù)據(jù)。

ServerSocketChannel

可以對標(biāo) ServerSocket,也就是服務(wù)端創(chuàng)建的 Socket。

它的作用就是監(jiān)聽新建連的 TCP 連接,為新進(jìn)一個(gè)連接創(chuàng)建對應(yīng)的 SocketChannel。

之后,通過新建的 SocketChannel 就可以進(jìn)行網(wǎng)絡(luò)數(shù)據(jù)的讀寫,與對端交互。

可以看到它主要是用來接待新連接,這功能主要就是服務(wù)端做的,所以叫 ServerSocketChannel。

DatagramChannel

看到 Datagram 應(yīng)該就知道是 UDP 協(xié)議了,是無連接協(xié)議。

利用 DatagramChannel 可以直接通過 UDP 進(jìn)行網(wǎng)絡(luò)數(shù)據(jù)的讀寫。

FileChannel

文件通道,用來進(jìn)行文件的數(shù)據(jù)讀寫。

我們?nèi)粘i_發(fā)主要是基于 TCP 協(xié)議,所以我們把精力放在 SocketChannel 和 ServerSocketChannel 上即可。

我們再回過頭來繼續(xù)看看 SocketChannel 和 ServerSocketChannel。

SocketChannel 主要在兩個(gè)地方出現(xiàn):

  • 客戶端,客戶端創(chuàng)建一個(gè) SocketChannel 用于連接至遠(yuǎn)程的服務(wù)端。
  • 服務(wù)端,服務(wù)端利用 ServerSocketChannel 接收新連接之后,為其創(chuàng)建一個(gè) SocketChannel 。

隨后,客戶端和服務(wù)端就可以通過這兩個(gè) SocketChannel 相互發(fā)送和接收數(shù)據(jù)。

ServerSocketChannel 主要出現(xiàn)在一個(gè)地方:服務(wù)端。

服務(wù)端需要綁定一個(gè)端口,然后監(jiān)聽新連接的到來,這個(gè)活兒就由 ServerSocketChannel 來干。

服務(wù)端內(nèi)常常會(huì)利用一個(gè)線程,一個(gè)死循環(huán),不斷地接收新連接的到來。

  1. ServerSocketChannel serverSocketChannel
  2. = ServerSocketChannel.open();
  3. ......
  4. while(true){
  5. // 接收的新連接
  6. SocketChannel socketChannel =
  7. serverSocketChannel.accept();
  8. .......
  9. }

至此,想必你應(yīng)該清楚 ServerSocketChannel 和 SocketChannel 的區(qū)別和作用了。

Buffer

Buffer 說白了就是內(nèi)存中可以讀寫的一塊地方,叫緩沖區(qū),用于緩存數(shù)據(jù)。

其實(shí)還真沒啥好說的,最多就講講 Java NIO Buffer 的 API。

但講 API 的太死板了,所以自己上網(wǎng)搜搜吧。我就告知一個(gè)結(jié)論,這個(gè) API 很不好用,稍微漏寫了點(diǎn),就容易出 bug,而且還有很多優(yōu)化的之處,所以 Netty 沒用 Java NIO Buffer 而是自己實(shí)現(xiàn)了一個(gè) Buffer,叫 ByteBuf。

等我們之后分析 ByteBuf 的時(shí)候再來盤一盤?,F(xiàn)在你只需要知道 Buffer 主要用來緩存通道的讀寫數(shù)據(jù)即可。

對了,看到這可能會(huì)有人提出疑問,為什么 Channel 必須和 Buffer 搭配使用?

其實(shí)網(wǎng)絡(luò)數(shù)據(jù)是面向字節(jié)的,但是我們讀寫的數(shù)據(jù)往往是多字節(jié)的,假設(shè)不用 Buffer ,那我們就得一個(gè)字節(jié)一個(gè)字節(jié)的調(diào)用讀和調(diào)用寫,想想是不是很麻煩?

所以我們搞個(gè) Buffer,把數(shù)據(jù)攏一攏,這樣之后的調(diào)用才能更好地處理完整的數(shù)據(jù),方便異步的處理等等。

Selector

I/O多路復(fù)用的核心玩意。

一個(gè) Selector 上可以注冊多個(gè) Channel ,我們從上面得知一個(gè) Channel 就對應(yīng)了一個(gè)連接,因此一個(gè) Selector 可以管理多個(gè) Channel 。

具體管理什么?

當(dāng)任意 Channel 發(fā)生讀寫事件的時(shí)候,通過 Selector.select() 就可以捕捉到事件的發(fā)生,因此我們利用一個(gè)線程,死循環(huán)的調(diào)用 Selector.select(),這樣可以利用一個(gè)線程管理多個(gè)連接,減少了線程數(shù),減少了線程的上下文切換和節(jié)省了線程資源。

這就是 Selector 的核心功能,然后我們再來細(xì)說具體是怎樣管理的。

首先,創(chuàng)建一個(gè) Selector。

  1. Selector selector = Selector.open();

然后,你需要將被管理的 Channel 注冊到 Selector 上,并聲明感興趣的事件。

  1. SelectionKey key = channel.register(selector, Selectionkey.OP_READ);

Netty 基石,Java NIO 核心知識(shí)

事件一共有以上四種類型,注冊的時(shí)候可以同時(shí)對多種類型的事件感興趣,例如:

  1. SelectionKey key
  2. = channel.register(selector,
  3. Selectionkey.OP_READ | SelectionKey.OP_WRITE);

這樣,當(dāng)這個(gè) Channel 發(fā)生讀或?qū)懯录?,我們調(diào)用 Selector.select() 就可以得知有事件發(fā)生。

具體 Selector.select() 有三個(gè)重載方法:

  • int selectNow(),不論是否有無事件發(fā)生,立即返回
  • int select(long timeout),至多阻塞 timeout 時(shí)間(或被喚醒),如果提早有事件發(fā)生,提早返回
  • int select(),一直阻塞著,直到有事件發(fā)生(或被喚醒)

返回值就是就緒的通道數(shù),一般判斷大于 0 即可進(jìn)行后續(xù)的操作。

后續(xù)的操作就是調(diào)用:

  1. Set selectedKeys = selector.selectedKeys();

獲得了一個(gè)類型為 Set 的 selectedKeys 集合,那這個(gè) selectedKeys 又是啥玩意?

我們來看一下它的方法和成員:

Netty 基石,Java NIO 核心知識(shí)

看到這些成員,其實(shí)我們就很清晰了,我們可以通過 selectedKey 得知當(dāng)前發(fā)生的是什么事件,有 isAcceptable、isReadable 等等。

然后還能獲得對應(yīng)的 channel 進(jìn)行相應(yīng)的讀寫操作,還有獲取 attachment 等等。

所以得到了 selectedKeys 就可以通過迭代器遍歷所有發(fā)生事件的連接,然后進(jìn)行操作。

大致使用的代碼如下所示:

  1. while(true) {
  2. int readyNum = selector.select();
  3. if (readyNum == 0) {
  4. continue;
  5. }
  6. Set selectedKeys = selector.selectedKeys();
  7. Iterator keyIterator = selectedKeys.iterator();
  8. while(keyIterator.hasNext()) {
  9. SelectionKey key = keyIterator.next();
  10. if(key.isAcceptable()) {
  11. // a connection was accepted by a ServerSocketChannel.
  12. } else if (key.isConnectable()) {
  13. // a connection was established with a remote server.
  14. } else if (key.isReadable()) {
  15. // a channel is ready for reading
  16. } else if (key.isWritable()) {
  17. // a channel is ready for writing
  18. }
  19. keyIterator.remove(); //執(zhí)行完畢之后,需要在循環(huán)內(nèi)移除自己
  20. }
  21. }

還有個(gè)方法就是 Selector.wakeup(),可以喚醒阻塞著的 Selector。

對了還有一點(diǎn)沒說,就是如果 Channel 要和 Selector 搭配,那它必須得是非阻塞的,即配置

  1. channel.configureBlocking(false);

從上面的操作,我們可以得知 Selector 處理事件的時(shí)候必須快,如果長時(shí)間處理某個(gè)事件,那么注冊到 Selector 上的其他連接的事件就不會(huì)被及時(shí)處理,造成客戶端阻塞。

至此,想必你應(yīng)該清晰 Selector 具體是如何管理這么多連接的了。

參考:https://ifeve.com/java-nio-all/

原文鏈接:https://mp.weixin.qq.com/s/0p3hId2GCy48yyGH55A5Qg

延伸 · 閱讀

精彩推薦
  • Java教程Java8中Stream使用的一個(gè)注意事項(xiàng)

    Java8中Stream使用的一個(gè)注意事項(xiàng)

    最近在工作中發(fā)現(xiàn)了對于集合操作轉(zhuǎn)換的神器,java8新特性 stream,但在使用中遇到了一個(gè)非常重要的注意點(diǎn),所以這篇文章主要給大家介紹了關(guān)于Java8中S...

    阿杜7482021-02-04
  • Java教程Java實(shí)現(xiàn)搶紅包功能

    Java實(shí)現(xiàn)搶紅包功能

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)搶紅包功能,采用多線程模擬多人同時(shí)搶紅包,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙...

    littleschemer13532021-05-16
  • Java教程升級(jí)IDEA后Lombok不能使用的解決方法

    升級(jí)IDEA后Lombok不能使用的解決方法

    最近看到提示IDEA提示升級(jí),尋思已經(jīng)有好久沒有升過級(jí)了。升級(jí)完畢重啟之后,突然發(fā)現(xiàn)好多錯(cuò)誤,本文就來介紹一下如何解決,感興趣的可以了解一下...

    程序猿DD9332021-10-08
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    這篇文章主要介紹了Java使用SAX解析xml的示例,幫助大家更好的理解和學(xué)習(xí)使用Java,感興趣的朋友可以了解下...

    大行者10067412021-08-30
  • Java教程Java BufferWriter寫文件寫不進(jìn)去或缺失數(shù)據(jù)的解決

    Java BufferWriter寫文件寫不進(jìn)去或缺失數(shù)據(jù)的解決

    這篇文章主要介紹了Java BufferWriter寫文件寫不進(jìn)去或缺失數(shù)據(jù)的解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望...

    spcoder14552021-10-18
  • Java教程xml與Java對象的轉(zhuǎn)換詳解

    xml與Java對象的轉(zhuǎn)換詳解

    這篇文章主要介紹了xml與Java對象的轉(zhuǎn)換詳解的相關(guān)資料,需要的朋友可以參考下...

    Java教程網(wǎng)2942020-09-17
  • Java教程小米推送Java代碼

    小米推送Java代碼

    今天小編就為大家分享一篇關(guān)于小米推送Java代碼,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧...

    富貴穩(wěn)中求8032021-07-12
  • Java教程20個(gè)非常實(shí)用的Java程序代碼片段

    20個(gè)非常實(shí)用的Java程序代碼片段

    這篇文章主要為大家分享了20個(gè)非常實(shí)用的Java程序片段,對java開發(fā)項(xiàng)目有所幫助,感興趣的小伙伴們可以參考一下 ...

    lijiao5352020-04-06
主站蜘蛛池模板: 日本成人午夜视频 | 欧美一级α片 | 在线免费小视频 | 中文字幕免费一区 | 欧美成人小视频 | 激情久久一区二区 | www.成人免费视频 | av成人免费| 日韩黄色三级视频 | 黄网在线 | 久久青草影院 | 久久综合婷婷 | 免费观看视频在线 | 亚洲精品成人久久 | 欧美一级做性受免费大片免费 | 欧美日韩在线视频一区 | 免费色片 | 国产91九色在线播放 | 成人av一区二区免费播放 | 91精品国产99久久久久久红楼 | 久久久久久亚洲国产精品 | 在线播放av网址 | 在线成人www免费观看视频 | 污黄视频在线观看 | a视频网站 | 成人免费网站在线观看 | 福利在线国产 | 久久丝袜脚交足黄网站免费 | 国产成人在线免费观看视频 | 国产jjizz一区二区三区视频 | 成人免费看片a | 操操操日日日干干干 | 久久久经典视频 | 精品国产一区二区久久 | 精品国产乱码久久久久久久久 | 欧美激情猛片xxxⅹ大3 | 叶子楣成人爽a毛片免费啪啪 | av手机免费在线观看 | 成人在线观看一区 | 国产精选久久久 | 久久精品一级片 |