成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

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

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

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

你好,我是yes。

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

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

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

  • Channel
  • Buffer
  • Selector

什么是 Channel

翻譯過(guò)來(lái)就是通道。

我們可以往通道里寫數(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

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

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

ServerSocketChannel

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

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

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

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

DatagramChannel

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

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

FileChannel

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

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

我們?cè)倩剡^(guò)頭來(lái)繼續(xù)看看 SocketChannel 和 ServerSocketChannel。

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

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

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

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

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

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

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

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

Buffer

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

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

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

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

對(duì)了,看到這可能會(huì)有人提出疑問(wèn),為什么 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 上可以注冊(cè)多個(gè) Channel ,我們從上面得知一個(gè) Channel 就對(duì)應(yīng)了一個(gè)連接,因此一個(gè) Selector 可以管理多個(gè) Channel 。

具體管理什么?

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

這就是 Selector 的核心功能,然后我們?cè)賮?lái)細(xì)說(shuō)具體是怎樣管理的。

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

  1. Selector selector = Selector.open(); 

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

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

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

  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(),不論是否有無(wú)事件發(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 又是啥玩意?

我們來(lái)看一下它的方法和成員:

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

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

所以得到了 selectedKeys 就可以通過(guò)迭代器遍歷所有發(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.  } 

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

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

  1. channel.configureBlocking(false); 

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

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

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

 

責(zé)任編輯:武曉燕 來(lái)源: yes的練級(jí)攻略
相關(guān)推薦

2021-09-27 08:56:44

NettyChannelHand架構(gòu)

2017-03-07 13:03:34

AndroidView知識(shí)問(wèn)答

2024-03-04 08:10:00

C#多線程語(yǔ)言

2020-11-06 00:50:16

JavaClassLoaderJVM

2024-07-29 11:21:24

2024-11-04 09:00:00

Java開(kāi)發(fā)

2021-12-30 08:17:27

Springboot數(shù)據(jù)訪問(wèn)DataSourceB

2025-01-07 14:10:46

SpringBoot開(kāi)發(fā)Java

2021-09-06 08:31:11

Kafka架構(gòu)主從架構(gòu)

2021-01-15 08:35:49

Zookeeper

2020-10-10 19:37:27

BIO 、NIO 、A

2018-07-26 20:10:02

編程語(yǔ)言Java多線程

2020-10-26 10:40:31

Axios前端攔截器

2021-01-06 13:52:19

zookeeper開(kāi)源分布式

2025-03-26 11:30:40

2025-05-13 08:10:00

MySQL二進(jìn)制日志binlog

2022-11-04 09:01:45

HashMap函數(shù)擴(kuò)容鏈

2024-04-23 14:25:16

Python備忘清單

2022-04-16 16:52:24

Netty網(wǎng)絡(luò)服務(wù)器客戶端程序

2020-05-19 14:40:08

Linux互聯(lián)網(wǎng)核心
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 在线免费观看a级片 | 男女污污动态图 | 四虎在线视频 | 在线国产视频 | 亚洲精品综合 | 在线天堂免费中文字幕视频 | 久久久久久久久久久久久9999 | 综合精品在线 | 日韩精品中文字幕在线 | 欧美亚洲成人网 | 久久精品久久久 | 成人在线播放 | 久久国产成人 | 羞羞的视频在线观看 | 亚洲aⅴ| 美国黄色毛片 | 神马久久久久久久久久 | 亚洲国产成人av | 黄页网址在线观看 | 可以免费观看的av | 91伦理片| 日日夜夜天天久久 | 国产午夜三级一区二区三 | 天天插天天射天天干 | 91大神在线资源观看无广告 | 成人av网站在线观看 | 亚洲精品资源 | 国产欧美一区二区三区在线看 | 男人久久天堂 | 久久精品一 | 91影片| 久草精品在线 | 久操av在线| 99精品久久99久久久久 | 黄色毛片黄色毛片 | 亚洲视频一区二区三区 | 一级在线视频 | 91精品久久久久久久久久入口 | 在线观看中文字幕一区二区 | a级片网站 | 国产一区2区 |