NIO網絡通信基礎

文章目錄

  • 概述
  • 一、Socket
  • 二、NIO三大組件與事件
  • 三、Reactor模式
  • 四、NIO通信案例
    • 4.1、服務端
    • 4.2、客戶端


本文為個人學習筆記整理,僅供交流參考,非專業教學資料,內容請自行甄別

概述

??前篇中提到,BIO是阻塞的IO,阻塞體現在建立連接和通信時,并且線程模型是1:1的。即使使用線程池進行處理,也受限于最大線程數以及cpu上下文的切換。
??NIO則是非阻塞的IO,利用了Reactor反應器模式和多路復用機制。可以實現服務端一個線程應對多個客戶端的連接和請求而不阻塞。

一、Socket

??在計算機網絡中,TCP、UDP是運輸層的協議,應用層需要依靠運輸層提供服務。Socket是位于應用層和運輸層之間的一個中間軟件抽象層,屏蔽了運輸層協議的一些實現細節,例如TCP的三次握手,四次揮手,滑動窗口算法等。
??從設計模式的角度來看,Socket屬于典型的外觀模式,用戶無需關心底層的具體實現細節,通過Socket提供的API即可實現網絡編程。
??主機 A 的應用程序要能和主機 B 的應用程序通信,必須通過 Socket 建立連接,無論是客戶端連接上服務端,還是服務端接收了客戶端的連接,都會產生一個Socket 實例:
在這里插入圖片描述
創建服務端
在這里插入圖片描述
接收客戶端的連接
在這里插入圖片描述
啟動客戶端

二、NIO三大組件與事件

??NIO的三大組件:

  • Selector:選擇器,客戶端和服務端會將自己感興趣的事件注冊到選擇器上,在有事件到達時,選擇器通知訂閱者去執行相應的操作。(Reactor模式),通過一個Selector去管理多個Channel,就稱為NIO的多路復用機制
  • Channel:通道,是應用間傳遞數據的渠道,是雙工通信,應用程序可以通過通道讀取數據,也可以通過通道向操作系統寫數據,而且可以同時進行讀寫。
  • Buffer:緩沖區,BIO和NIO的區別其一就是,BIO是面向字節流的,而NIO是面向緩沖區的。數據是從通道讀入緩沖區,從緩沖區寫入到通道中。
    • 在寫入時,應用程序將數據寫入Buffer,再通過Channel將Buffer中的數據傳遞出去。
    • 在讀取時,先將Channel中的數據讀入Buffer,應用程序再從Buffer中讀取數據。

??與Selector和Channel相關的,還有一個SelectionKey。SelectionKey是一個抽象類,維護了Selector和Channel的關系。SelectionKey定義了四個事件:
在這里插入圖片描述

  • OP_READ:讀事件,當緩沖區中有數據可讀時,觸發該事件,通常需要主動去關注,以接收到緩沖區有數據可讀的通知,并進行處理。
  • OP_WRITE:寫事件,當緩沖區有空閑時,就會觸發該事件。一般情況下是無需關注該事件的,因為緩沖區不會被占滿,關注該事件浪費CPU資源。
  • OP_CONNECT:連接事件,當SocketChannel.connect()請求連接成功后就緒,只允許客戶端關注。
  • OP_ACCEPT:接收連接事件,當服務器接收到客戶端的連接后就需,只允許服務端關注。

??對于服務端而言,通常是ServerSocketChannel創建后注冊OP_ACCEPT事件,在接收到客戶端的連接,產生SocketChannel后,關注OP_READ事件。
在這里插入圖片描述
在這里插入圖片描述
??對于客戶端而言,通常是SocketChannel創建后,執行連接服務端的操作,因為通過SocketChannelconfigureBlocking方法,將通道設置成了非阻塞,所以調用connect()方法可能立即返回,而不會等待連接建立。連接過程在后臺異步進行,線程可以繼續執行其他任務,就要根據connect()方法的返回值,執行不同的操作:

  • 如果connect()方法返回true,代表連接建立成功,就關注OP_READ事件。
  • 如果connect()方法返回false,代表連接沒有建立完成,繼續關注OP_CONNECT事件。

在這里插入圖片描述
??后續在進行事件處理時,如果關注了OP_CONNECT事件,就再次判斷通道是否建立完成,如果建立完成,就關注OP_READ事件。
在這里插入圖片描述

三、Reactor模式

??Reactor模式是NIO的底層實現機制,體現在NIO的Selector選擇器上。
在這里插入圖片描述
圖片來源:圖靈學院

??Reactor模式的思想,簡單來說,就是用戶向選擇器進行注冊,告訴選擇器自己所關心的事件,當對應的事件發生時,再由選擇器去通知用戶,然后用戶執行自己的操作。所以選擇器是Reactor模式的核心組件。
??Reactor模式主要由兩個核心部分組成:

  1. Reactor:負責監聽和分發I/O事件(如連接事件、讀寫事件)。它運行在一個獨立線程中,通過選擇器(Selector)實現I/O多路復用,不斷輪詢注冊的事件源。當事件就緒時,Reactor將其分發給對應的事件處理器。
  2. 處理資源池:通常是一個線程池,負責執行事件處理邏輯(如讀取數據、執行業務邏輯、發送響應)。這避免了耗時操作阻塞Reactor線程。

??假設一個Web服務器使用Reactor模式:
3. 主線程(Reactor線程)注冊SocketChannel的READ事件,并調用selector.select()阻塞。
4. 同時,一個工作線程(來自處理資源池)可以處理之前接收到的HTTP請求(如解析數據、訪問緩存)。
5. 當新數據到達時,Reactor線程喚醒,派發READ事件給處理器,然后工作線程接管處理。

??Reactor模式也有三種實現:

  1. 單線程的reactor:連接,讀取,發送數據,處理業務,都在一個線程中執行。
  2. 多線程工作reactor:連接,讀取,發送數據,由一個線程完成,業務工作由線程池去處理。
  3. 多線程主從reactor:連接由一個線程完成,讀取,發送數據,由另一個線程完成,業務工作由線程池去處理。

四、NIO通信案例

4.1、服務端

public class NioServerDemo {public static void main(String[] args) throws IOException {//netty服務端 開啟ssc//everSocketChannel:對連接建立的事件感興趣,是對SeverSocket的包裝。ServerSocketChannel ssc = ServerSocketChannel.open();//開啟選擇器Selector selector = Selector.open();//設置為非阻塞ssc.configureBlocking(false);//綁定端口ssc.socket().bind(new InetSocketAddress(8080));//Selector登記SSC對連接事件感興趣。//SelectionKey是SSC和SC向Selector注冊的相關的事件的代號。標記了不同的事件。讀,寫,連接,接受連接事件。ssc.register(selector, SelectionKey.OP_ACCEPT);while (true) {//獲取當前有哪些事件selector.select(1000);Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();while (iterator.hasNext()) {SelectionKey key = iterator.next();//防止事件重復被處理iterator.remove();//進行處理handle(key, selector);}}}private static void handle(SelectionKey key, Selector selector) throws IOException {if (key.isValid()) {/*處理新加入客戶端的請求假設此時有新連接到達Selector*/if (key.isAcceptable()) {//拿到服務端的ssc//Selector將新連接分配給SSC。ServerSocketChannel ssc = (ServerSocketChannel) key.channel();//接受請求,得到sc//SSC接收完連接后,和客戶端進行三次握手,產生一個Socket,然后將Socket包裝成SCSocketChannel sc = ssc.accept();System.out.println("==========建立連接=========");//同樣設置為非阻塞sc.configureBlocking(false);//Selector登記SC對讀事件感興趣//關注讀事件sc.register(selector, SelectionKey.OP_READ);}/*處理讀事件假設此時客戶端發送了數據到Selector,通知SC有數據過來了。*/ else if (key.isReadable()) {//sc:socketchannel 處理讀事件//由SC和客戶端進行實際的網絡讀寫。SocketChannel sc = (SocketChannel) key.channel();//創建緩沖區//并非直接將數據交付給應用層,而是經過buffer緩沖區。發送數據也是要經過buffer。//buffer本質上是內存中的一塊區域,需要讀寫模式切換ByteBuffer byteBuffer = ByteBuffer.allocate(1024);//從通道讀消息,寫入緩沖區int bytes = sc.read(byteBuffer);if (bytes > 0) {//切換模式byteBuffer.flip();byte[] bs = new byte[byteBuffer.remaining()];byteBuffer.get(bs);String message = new String(bs, StandardCharsets.UTF_8);System.out.println("服務器收到消息:" + message);//發送應答消息到客戶端doWrite(sc, message);} else if (bytes < 0) {/*取消特定的注冊關系*/key.cancel();/*關閉通道*/sc.close();}}}}/*發送應答消息*/private static void doWrite(SocketChannel sc, String response) throws IOException {byte[] bytes = response.getBytes();ByteBuffer buffer = ByteBuffer.allocate(bytes.length);//將服務端的響應存入buffer中buffer.put(bytes);//切換模式buffer.flip();//寫入客戶端(從buffer中讀,寫入對方 )sc.write(buffer);}}

4.2、客戶端

public class NioClientDemo {private static SocketChannel sc;public static void main(String[] args) throws IOException {new Thread(()->{try {client();} catch (IOException e) {throw new RuntimeException(e);}}).start();Scanner scanner = new Scanner(System.in);String next = scanner.next();doWrite(sc,next);}private static void client() throws IOException {//創建選擇器Selector selector = Selector.open();//創建客戶端通道sc = SocketChannel.open();//設置通道為非阻塞sc.configureBlocking(false);//非阻塞的連接//如果sc.connect返回true,代表連接已經建立完成if (sc.connect(new InetSocketAddress("127.0.0.1", 8080))) {//可以關注讀取事件sc.register(selector, SelectionKey.OP_READ);}//否則連接還沒有建立完成else {//繼續關注建立連接事件sc.register(selector, SelectionKey.OP_CONNECT);}//輪詢選擇器while (true) {selector.select(1000);Iterator<SelectionKey> it = selector.selectedKeys().iterator();while (it.hasNext()) {SelectionKey sk = it.next();it.remove();if (sk.isValid()) {//獲得關心當前事件的channelSocketChannel socketChannel = (SocketChannel) sk.channel();//連接事件if (sk.isConnectable()) {//查看通道連接是否建立完成if (sc.finishConnect()) {//建立完成,注冊讀事件socketChannel.register(selector,SelectionKey.OP_READ);} else{System.exit(1);}}else if (sk.isReadable()){//創建ByteBuffer,并開辟一個1M的緩沖區ByteBuffer buffer = ByteBuffer.allocate(1024);//將channel中的數據讀取到buffer中int readBytes = sc.read(buffer);//讀取到字節,對字節進行編解碼if(readBytes>0){//將緩沖區當前的limit設置為position,position=0,// 用于后續對緩沖區的讀取操作buffer.flip();//根據緩沖區可讀字節數創建字節數組byte[] bytes = new byte[buffer.remaining()];//將緩沖區可讀字節數組復制到新建的數組中buffer.get(bytes);String result = new String(bytes,"UTF-8");System.out.println("客戶端收到消息:" + result);}//鏈路已經關閉,釋放資源else if(readBytes<0){sk.cancel();sc.close();}}}}}}private static void doWrite(SocketChannel channel,String request)throws IOException {//將消息編碼為字節數組byte[] bytes = request.getBytes();//根據數組容量創建ByteBufferByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);//將字節數組復制到緩沖區writeBuffer.put(bytes);//flip操作writeBuffer.flip();//發送緩沖區的字節數組/*關心事件和讀寫網絡并不沖突*/channel.write(writeBuffer);}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/89459.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/89459.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/89459.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Redis4緩存穿透:布隆過濾器與空對象方案

緩存穿透緩存穿透是指客戶端請求的數據在緩存中和數據庫中都不存在&#xff0c;這樣緩存永遠不會生效&#xff0c;這些請求都會達到數據庫。1)方案1&#xff1a;緩存空對象在緩存中存儲一個空值每次讀取這個空優點&#xff1a;實現簡單&#xff0c;維護方便缺點&#xff1a;造成…

域名WHOIS信息查詢免費API使用指南

本文介紹由接口盒子提供的免費域名WHOIS查詢API服務&#xff0c;幫助開發者快速獲取域名的注冊信息、到期時間、DNS服務器等關鍵數據。 一、接口基本信息 ?功能說明?&#xff1a;查詢頂級域名的WHOIS信息&#xff08;不支持國別域名/中文域名&#xff09;?請求地址?&#…

【18位數據次方提高數據輸出速度】2022-3-9

實在是無法忍受W10輸出數據那么慢W7需要2分鐘輸出數據W10則需要10分鐘完成W7需要3分鐘W10則需要15分鐘完成輸出數據&#xff0c;雖然W10運算速度比W7快很多但是加上輸出速度總體完成時間居然差不多&#xff01;隨著使用數組超過百萬W7數據輸出時間也變長&#xff0c;隨著數組數…

云原生技術與應用-Kubernetes架構原理與集群環境部署

目錄 一.為什么需要kubernetes 1.對于開發人員 2.對于運維人員 二.kubernetes帶來的挑戰 三.kubernetes架構解析 1.master節點的組件 2.node節點包含的組件 3.kubernetes網絡插件 四.kubernetes快速安裝kubernetes集群 1.部署docker環境 2.部署kubernetes集群 五.Metrics-…

百度權重提升技巧分析:從底層邏輯到實戰策略

在搜索引擎優化&#xff08;SEO&#xff09;領域&#xff0c;百度權重始終是網站運營者關注的核心指標之一。它不僅反映了網站在百度搜索中的綜合表現&#xff0c;更直接影響著流量獲取能力與商業價值。然而&#xff0c;百度權重并非百度官方直接公布的數據&#xff0c;而是第三…

模擬數據生成---使用NGS數據模擬軟件VarBen

目錄 1.在BAM文件中根據指定的變異等位基因分數的指定位置或區域隨機選擇read。 2.篩選變異等位基因分數的reads: 3.裝BWA和samtools軟件包(samtools在linux系統中下載過,前文有講過) 4.寫py腳本 5.下載pysam庫模塊 6.下載參考基因組hg38 7.解壓gz 8.建立samtools索引…

Redis-典型應用-分布式鎖

目錄 1.什么是分布式鎖? 2.分布式鎖的實現 3.引入過期時間 4.引入校驗ID 5.引入lua腳本: 6.引入看門狗(watch dog) 7.引入redislock算法: 1.什么是分布式鎖? 在 分布式系統中,會出現多個節點同時訪問同一個公共資源, 此時就需要通過鎖來作互斥控制,避免出現類似于多線程…

Dinky (Mac) 本地開發環境搭建指南

目錄 一、前置條件 二、代碼準備 三、前端環境搭建 1. 安裝Node環境 2. 安裝PNPM 3. 構建前端 四、后端環境搭建 1. 本地編譯依賴 2. 添加必要依賴 3. 啟動后端服務 五、訪問系統 附錄&#xff1a;官方參考 一、前置條件 確保已安裝以下軟件&#xff1a; 軟件要求…

Java Set 集合詳解:從基礎語法到實戰應用,徹底掌握去重與唯一性集合

作為一名 Java 開發工程師&#xff0c;你一定在實際開發中遇到過需要去重、唯一性校驗、快速查找等場景。這時候&#xff0c;Set 集合 就成為你不可或缺的工具。本文將帶你全面掌握&#xff1a;Set 接口的定義與核心方法常見實現類&#xff08;如 HashSet、TreeSet、LinkedHash…

在分布式系統中,如何保證緩存與數據庫的數據一致性?

口訣&#xff1a; 讀多寫少用旁路&#xff0c;先更庫再刪緩存&#xff1b; 強一致選寫透&#xff0c;緩存代理更庫走&#xff1b; 性能優先用寫回&#xff0c;異步批量有風險&#xff1b; 高并發加雙刪&#xff0c;延遲兜底防舊殘&#xff1b; 強一致用鎖串&#xff0c;并發雖低…

【洛谷P1417】烹調方案 題解

題目大意 一共有 nnn 件食材&#xff0c;每件食材有三個屬性&#xff0c;aia_iai?&#xff0c;bib_ibi? 和 cic_ici?&#xff0c;如果在 ttt 時刻完成第 iii 樣食材則得到 ai?tbia_i-t\times b_iai??tbi? 的美味指數&#xff0c;用第 iii 件食材做飯要花去 cic_ici? 的…

vue svg實現一個環形進度條組件

svg實現一個環形進度條設計初衷&#xff1a;本來想直接使用element的進度條組件的&#xff0c;但是好多屬性都沒有辦法控制。 UI設計的圖如下&#xff0c;需要控制未完成和已完成的顏色&#xff0c;端點的形狀改為普通的butt 所以使用svg實現了一個環形進度條組件element組件設…

02 51單片機之LED閃爍

文章目錄1、單片機1-1、簡介1-2、應用場景2、51單片機2-1、背景2-2、主要品牌及其產品2-3、基本組成2-4、命名規則3、單片機內部結構3-1、單片機內部結構圖3-2、單片機內部結構3-3、單片機內部管腳圖3-4、單片機最小系統3-5、開發板介紹4、點亮LED4-1、新建工程4-1-1、創建工程…

Typecho博客集成算術驗證碼防御垃圾評論實戰指南

文章目錄 Typecho實現算術驗證碼防御機器人垃圾評論的完整方案 背景與問題分析 技術方案設計 系統架構 技術選型 核心實現步驟 1. 創建驗證碼生成函數 2. 修改評論表單模板 3. 添加AJAX刷新功能 4. 創建驗證碼刷新接口 5. 添加評論提交驗證 安全增強措施 1. 防止暴力破解 2. 增…

clonezilla 導出自動化恢復iso

clonezilla 下載及U盤工具下載 clonezilla rufus U盤寫入工具ventoy U盤工具downloaddownloaddownload clonezilla 備份&#xff0c;連貫上一篇文章參考 Choose Clonezilla live (VGA 800x600) Wait for it to complete Language selection Keyboard Settings Select Mode …

深度學習模型開發部署全流程:以YOLOv11目標檢測任務為例

深度學習模型開發部署全流程&#xff1a;以YOLOv11目標檢測任務為例 深度學習模型從開發到部署的完整流程包含需求分析、數據準備、模型訓練、模型優化、模型測試和部署運行六大核心環節。YOLOv11作為新一代目標檢測模型&#xff0c;不僅延續了YOLO系列的高效實時性能&#xff…

單片機(STM32-串口通信)

一、串口通信基礎概念串口通信&#xff08;Serial Communication&#xff09;是一種在計算機和外部設備之間進行數據傳輸的通信方式。它通過串行方式逐位傳輸數據&#xff0c;是最基本和常用的通信接口之一。主要特點1. 串行傳輸(1)數據按位順序傳輸&#xff0c;一次只能傳輸一…

Redis學習其三(訂閱發布,主從復制,哨兵模式)

文章目錄9.Redis訂閱與發布9.1發布訂閱命令9.2示例10.Redis主從復制10.1概念10.2環境配置10.3集群搭建&#xff08;一主二從配置&#xff09;10.4使用規則&原理11.哨兵模式11.1基本概念11.2工作原理11.3使用案例12.緩存穿透,雪崩&#xff08;待拓展&#xff09;12.1緩存穿透…

跨平臺 App 如何無痛遷移到鴻蒙系統?全流程實戰+Demo 教程

摘要 目前&#xff0c;隨著 HarmonyOS&#xff08;鴻蒙系統&#xff09;的快速發展&#xff0c;越來越多開發者和企業希望將已有的 Android、Flutter、React Native 等跨平臺應用遷移到鴻蒙生態中。鴻蒙不僅具備分布式能力、原生性能和統一的開發范式&#xff0c;還提供了豐富的…

智慧后廚檢測算法構建智能廚房防護網

智慧后廚檢測&#xff1a;構建安全潔凈廚房的智能解決方案背景&#xff1a;傳統后廚管理的痛點與智慧化需求餐飲行業后廚管理長期面臨操作規范難落實、安全隱患難察覺、衛生狀況難追溯等痛點。傳統人工巡檢效率低、覆蓋面有限&#xff0c;難以實現24小時無死角監管。例如&#…