Java網絡編程NIO

一、NIO是什么?

NIO可以說是比BIO更強大的IO,可以設置非阻塞模式(通過事件的方式監聽數據的到來)

  • BIO是基于socket通信,一個線程對應一個socket連接,讀取數據要一直等待

  • NIO是基于channel通信,一個線程管理一個selector,一個selector管理多個channel,這樣一個線程就能管理多個客戶端連接

二、NIO核心組件

?① Selector:選擇器,負責管理多個通道channel,通過監聽通道綁定的事件,及時處理通道數據

?② Channel:通道,一般一端確定,另一端操作緩沖區Buffer。可以是文件通道,網絡通道等

?③ Buffer:緩沖區,包括直接內存緩沖和JVM內存緩沖。直接內存緩沖是在操作系統的非應用程序內存申請的空間,不占用JVM內存;JVM內存緩沖就是在堆空間申請的緩沖區。

? ?由于IO無法直接操作應用內存,因此需要將應用內存數據拷貝到直接內存,如果使用直接內存,就可以少一次拷貝操作

組件模型圖:

三、常用API

(1)緩沖區Buffer

① 介紹

? position:下一次讀取或寫入的位置

? limit:數據末尾后一個位置

? capacitry:緩沖區容量

② 相關API

以ByteBuffer為例

//	申請操作系統的內存緩沖區
ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
//	獲取position、limit、capacity
int position = buffer.position();
int limit = buffer.limit();
int capacity = buffer.capacity();//	向緩沖區中寫入數據
String data = "hello, my name is jack!";
buffer.put(data.getBytes());//	切換模式,標定界限(limit=position,position=0)
buffer.flip();//	讀取緩沖區的數據
int dataLength = buffer.remaining();	//  獲取剩余數據量
String content = new String(buffer.array(), 0, dataLength);//	清空緩沖區,實際上是將各個下標設置為初始值,如:position=0,limit=capacity
buffer.clear();

(2)通道Channel

拷貝文件的案例

public void test02() throws Exception {//  利用通道進行文件讀寫String fileName1 = "E:\\code\\java\\io_learn\\io_02_nio\\src\\main\\resources\\1.jpg";String fileName2 = "E:\\code\\java\\io_learn\\io_02_nio\\src\\main\\resources\\2.jpg";//  打開文件輸入流FileInputStream fis = new FileInputStream(fileName1);//  通過輸入流獲取通道FileChannel fisChannel = fis.getChannel();//  打開文件輸出流FileOutputStream fos = new FileOutputStream(fileName2);//  通過輸出流獲取輸出通道FileChannel fosChannel = fos.getChannel();//  創建緩沖區ByteBuffer buffer = ByteBuffer.allocate(1024);//  將輸入通道的數據讀取到buffer,然后從buffer讀取數據到輸出通道while ((fisChannel.read(buffer)) > 0 ) {//  limit=position,position=0buffer.flip();//  將數據從buffer寫入到文件fosChannel.write(buffer);//  將各個下標指針歸還到最初位置buffer.clear();}//  釋放通道資源fisChannel.close();fosChannel.close();}

?利用transferFrom實現管道數據轉移

public void transferFromTest() throws Exception {/*利用transferFrom或transferTo實現文件的copy*/FileInputStream fis = new FileInputStream("E:\\code\\java\\io_learn\\io_02_nio\\src\\main\\resources\\1.jpg");FileChannel fisChannel = fis.getChannel();FileOutputStream fos = new FileOutputStream("E:\\code\\java\\io_learn\\io_02_nio\\src\\main\\resources\\11.jpg");FileChannel fosChannel = fos.getChannel();//  將數據轉移到目的通道fosChannel.transferFrom(fisChannel , 0, fisChannel.size());fisChannel.close();fosChannel.close();}

利用transferTo實現管道數據轉移

public void transferToTest() throws Exception {/*利用transferFrom或transferTo實現文件的copy*/FileInputStream fis = new FileInputStream("E:\\code\\java\\io_learn\\io_02_nio\\src\\main\\resources\\1.jpg");FileChannel fisChannel = fis.getChannel();FileOutputStream fos = new FileOutputStream("E:\\code\\java\\io_learn\\io_02_nio\\src\\main\\resources\\10.jpg");FileChannel fosChannel = fos.getChannel();// ?將數據轉移到目的通道fisChannel.transferTo(0, fisChannel.size(), fosChannel);fisChannel.close();fosChannel.close();}

注意:transferFrom和transferTo方法一次只能傳輸2G的數據(受操作系統限制)

(3)選擇器Selector

一般是通過服務通道監聽端口,等待客戶端的連接通道,然后將連接通道注冊到Selector,等待事件觸發通道行為

public void test() throws Exception {//  1.獲取通道ServerSocketChannel serverSocketChannel =ServerSocketChannel.open();//  2。設置為非阻塞模式serverSocketChannel.configureBlocking(false);//  3.綁定端口serverSocketChannel.bind(new InetSocketAddress(9898));//  4.獲取選擇器Selector selector = Selector.open();//  5.注冊通道到選擇器,通過客戶端建立通道的事件觸發serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);while (selector.select() > 0) {//  獲取到對應事件,進行處理...}}

?四、NIO實現聊天室案例

服務端Server

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;public class Server {public static void main(String[] args) throws Exception {//  1.創建服務端通道ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();//  2.設置為非阻塞模式serverSocketChannel.configureBlocking(false);//  3.綁定監聽端口serverSocketChannel.bind(new InetSocketAddress(9898));//  4.打開SelectorSelector selector =Selector.open();//  5.注冊監聽客戶端連接的通道serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);//  6.循環監聽連接等事件while (selector.select() > 0) {//  獲取事件迭代器Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();while (keyIterator.hasNext()) {//  獲取到具體事件SelectionKey key = keyIterator.next();//  判斷事件類型//  客戶端建立連接的事件if (key.isAcceptable()) {//  獲取到客戶端的連接通道SocketChannel socketChannel = serverSocketChannel.accept();//  設置通道為非阻塞模式socketChannel.configureBlocking(false);//  將連接通道注冊到選擇器socketChannel.register(selector, SelectionKey.OP_READ);}//  如果是讀取事件,證明客戶端發來了消息,進行數據讀取else if (key.isReadable()) {try {//  創建緩沖區ByteBuffer byteBuffer = ByteBuffer.allocate(1024);//  消息內容,正常聊天,消息一般很短,不會超過1kbString content = "";//  獲取到客戶端通道SocketChannel channel = (SocketChannel)key.channel();//  通過通道將數據讀取到bufferif (channel.read(byteBuffer) > 0) {//  切換為可讀模式byteBuffer.flip();//  將byte[]轉為stringcontent = new String(byteBuffer.array(), 0, byteBuffer.remaining());//  清空緩沖區byteBuffer.clear();}//  打印消息內容System.out.println(content);//  將數據寫入緩沖區byteBuffer.put(content.getBytes());//  將數據發送給其他所有非自己的客戶端Set<SelectionKey> keys = selector.keys();for (SelectionKey selectionKey : keys) {//  通過事件獲取到客戶端連接通道SelectableChannel selectableChannel = selectionKey.channel();//  如果通道是服務端監聽連接的通道ServerSocketChannel或者是自己,則不能轉發if (selectableChannel instanceof ServerSocketChannel || selectableChannel == channel) {continue;}//  將消息轉發給其他客戶端//  轉為客戶端channel類型SocketChannel otherClientChannel = (SocketChannel) selectableChannel;//  重新設置為讀模式byteBuffer.flip();//  發送消息otherClientChannel.write(byteBuffer);}} catch (Exception e) {//  出現異常,說明客戶端下線SocketChannel channel = (SocketChannel)key.channel();System.out.println("客戶端" + channel.getLocalAddress() + "離線");channel.close();//  釋放通道,接觸通道綁定的事件key.cancel();}}//  將使用完的事件進行移除keyIterator.remove();}}//  釋放資源serverSocketChannel.close();selector.close();}}

客戶端Client

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Scanner;public class Client {public static void main(String[] args) throws IOException {//  1.創建socket channelSocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));socketChannel.configureBlocking(false); //  設置為非阻塞模式//  2.開啟SelectorSelector selector = Selector.open();//  3.注冊通道,監聽通道的讀事件socketChannel.register(selector, SelectionKey.OP_READ);//  開啟一個接收服務端數據的線程new Thread(() -> {try {//  有事件,則執行處理,沒有事件則進行阻塞while(selector.select() > 0) {Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();//  遍歷事件,找到可讀事件,讀取服務端發來的數據while (keyIterator.hasNext()) {SelectionKey selectionKey = keyIterator.next();//  可讀事件,讀取服務端發來的數據if (selectionKey.isReadable()) {//  創建緩沖區ByteBuffer buffer = ByteBuffer.allocate(1024);//  讀取數據到緩沖區socketChannel.read(buffer);//  切換為讀模式buffer.flip();System.out.println("\n" + new String(buffer.array(), 0, buffer.remaining()));System.out.print("我的發言: ");}//  使用完畢的事件要進行移除keyIterator.remove();}}} catch (IOException e) {e.printStackTrace();}}).start();//  等待客戶的輸入,聊天控制臺ByteBuffer buffer = ByteBuffer.allocate(1024);Scanner sc = new Scanner(System.in);while (true) {System.out.print("我的發言: ");//  讀取用戶輸入String msg = socketChannel.getLocalAddress() + ": " + sc.nextLine();//  將消息放入緩沖區buffer.put(msg.getBytes());//  切換為寫模式buffer.flip();//  發送緩沖區的數據給服務噸socketChannel.write(buffer);//  清除本次數據buffer.clear();}}
}

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

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

相關文章

【動態規劃】二分優化最長上升子序列

最長上升子序列 II 題解 題目傳送門&#xff1a;AcWing 896. 最長上升子序列 II 一、題目描述 給定一個長度為 N 的數列&#xff0c;求數值嚴格單調遞增的子序列的長度最長是多少。 輸入格式&#xff1a; 第一行包含整數 N第二行包含 N 個整數&#xff0c;表示完整序列 輸…

Dify接口api對接,流式接收流式返回(.net)

試了好多種方法除了Console.WriteLine()能打印出來&#xff0c;試了好些方法都不行&#xff0c;不是報錯就是打印只有一行&#xff0c;要么就是接收完才返回...下面代碼實現調用api接收流式數據&#xff0c;并進行流式返回給前端&#xff1a; using Furion.HttpRemote; using …

19-元素顯示模式及浮動(CSS3)

知識目標 掌握標準文檔流的解析規則掌握元素的顯示模式掌握元素浮動屬性語法與使用掌握浮動塌陷解決方法 1. 標準文檔流 2. 元素顯示模式 元素顯示模式就是元素&#xff08;標簽&#xff09;以什么方式進行顯示&#xff0c;比如<div>獨占一行&#xff0c;一行可以放多…

HTML jQuery 項目 PDF 批注插件庫在線版 API 示例教程

本文章介紹 HTML && jQuery Web項目中 PDF 批注插件庫 ElasticPDF 在線版 API 示例教程&#xff0c;API 包含 ① 導出批注后PDF數據&#xff1b;② 導出純批注 json 數據&#xff1b;③ 加載舊批注&#xff1b;④ 切換文檔&#xff1b;⑤ 切換用戶&#xff1b;⑥ 清空批…

CATIA裝配體全自動存儲解決方案開發實戰——基于遞歸算法的產品結構樹批量處理技術

一、功能定位與技術架構 本工具針對CATIA V5裝配體文件管理場景&#xff0c;實現了一套全自動遞歸存儲系統&#xff0c;主要功能包括&#xff1a; ?智能路徑選擇&#xff1a;通過Tkinter目錄對話框實現可視化路徑選擇?產品結構遞歸解析&#xff1a;深度優先遍歷裝配體中的子…

C#:接口(interface)

目錄 接口的核心是什么&#xff1f; 1. 什么是接口&#xff08;Interface&#xff09;&#xff0c;為什么要用它&#xff1f; 2. 如何定義和使用接口&#xff1f; 3.什么是引用接口&#xff1f; 如何“引用接口”&#xff1f; “引用接口”的關鍵點 4. 接口與抽象類的區…

基于卷積神經網絡CNN實現電力負荷多變量時序預測(PyTorch版)

前言 系列專欄:【深度學習:算法項目實戰】?? 涉及醫療健康、財經金融、商業零售、食品飲料、運動健身、交通運輸、環境科學、社交媒體以及文本和圖像處理等諸多領域,討論了各種復雜的深度神經網絡思想,如卷積神經網絡、循環神經網絡、生成對抗網絡、門控循環單元、長短期記…

關于inode,dentry結合軟鏈接及硬鏈接的實驗

一、背景 在之前的博客 缺頁異常導致的iowait打印出相關文件的絕對路徑-CSDN博客 里 2.2.3 一節里&#xff0c;我們講到了file&#xff0c;fd&#xff0c;inode&#xff0c;dentry&#xff0c;super_block這幾個概念&#xff0c;在這篇博客里&#xff0c;我們針對inode和dentr…

游戲引擎學習第201天

倉庫:https://gitee.com/mrxiao_com/2d_game_5 回顧之前的內容&#xff0c;并遇到了一次一階異常&#xff08;First-Chance Exception&#xff09;。 歡迎來到新一期的開發過程&#xff0c;我們目前正在編寫調試接口代碼。 當前&#xff0c;我們已經在布局系統上進行了一些工…

計算機視覺算法實戰——基于YOLOv8的行人流量統計系統

?個人主頁歡迎您的訪問 ?期待您的三連 ? ?個人主頁歡迎您的訪問 ?期待您的三連 ? ?個人主頁歡迎您的訪問 ?期待您的三連? ??? ????????? ?? 引言:智能客流分析的市場需求 在零售、交通、安防等領域,準確的行人流量統計對于商業決策、公共安全管理…

Redis是什么?架構是怎么樣的?

目錄 前言 一,Redis架構 1.1 本地緩存 1.2 遠程緩存 二,強大的Redis優點 2.1 支持多種數據類型 2.2 內存過期策略 2.3 內存淘汰策略 2.4 持久化 三,Redis是什么 前言 我是一個程序員,維護了一個商品服務,它的背后直連Mysql數據庫,假設商品服務對外每秒需要提供1萬次…

藍橋杯真題——傳送陣

原題連接&#xff1a;藍橋杯2024年第十五屆省賽真題-傳送陣 - C語言網 知識點&#xff1a;并查集 題目描述 小藍在環球旅行時來到了一座古代遺跡&#xff0c;里面并排放置了 n 個傳送陣&#xff0c;進入第 i 個傳送陣會被傳送到第 ai 個傳送陣前&#xff0c;并且可以隨時選擇…

彩虹表攻擊

1. 引言 密碼安全一直是信息安全領域的重要課題。攻擊者可以利用**暴力破解(Brute-Force Attack)和字典攻擊(Dictionary Attack)等方式嘗試破解密碼。然而,計算機性能的提升使得這些方法的效率不斷提高,其中彩虹表攻擊(Rainbow Table Attack)**是一種極具威脅性的密碼…

Vue2 監聽器 watcher

文章目錄 前言監聽器的作用&#xff1a;工作流程&#xff1a;基本用法1. 簡單監聽2. 對象形式配置 使用場景1. 執行異步操作2. 監聽路由變化3. 復雜對象/數組變化 關鍵配置項與計算屬性的區別動態添加監聽器注意事項 前言 提示&#xff1a;這里可以添加本文要記錄的大概內容&a…

Linux系統程序設計:從入門到高級Day02

這一篇 我帶大家復習一下&#xff0c;C語言中的文件 那一部分 大家注意 這里的圖并非原創 是當時我老師的圖片 本片作用主要是 后續會有文件相關操作&#xff0c;這篇幫大家復習C語言文件中的內容 有助于大家后面的理解。 文章中代碼大多是圖片格式&#xff0c;是因為這是我…

N元語言模型的時間和空間復雜度計算

對于N元語言模型&#xff0c;時間復雜度是O(V ^ {N-1})&#xff0c;空間復雜度是O(V ^ {N})&#xff0c;N是詞匯表的大小。 空間復雜度&#xff1a;存儲所有可能的N-1元組及其對應的詞的頻次需要大量的存儲空間。例如&#xff0c;對于一個三元模型&#xff08;N3&#xff09;&…

Tmux 核心操作速查指南

Tmux 最常用操作筆記 1. 基本概念 會話&#xff08;Session&#xff09;&#xff1a;一個tmux會話可以包含多個窗口&#xff0c;適合長期任務管理。窗口&#xff08;Window&#xff09;&#xff1a;每個窗口是一個獨立的終端界面&#xff0c;可包含多個面板。面板&#xff08…

哈希表系列一>兩數之和

目錄 題目&#xff1a;方法&#xff1a;暴力代碼&#xff1a;優化后代碼&#xff1a; 題目&#xff1a; 鏈接: link 方法&#xff1a; 暴力代碼&#xff1a; public int[] twoSum(int[] nums, int target) {解法一&#xff1a;暴力解法&#xff1a;int n nums.length;for(int…

端到端機器學習流水線(MLflow跟蹤實驗)

目錄 端到端機器學習流水線(MLflow跟蹤實驗)1. 引言2. 項目背景與意義2.1 端到端機器學習流水線的重要性2.2 MLflow的作用2.3 工業級數據處理需求3. 數據集生成與介紹3.1 數據集構成3.2 數據生成方法4. 機器學習流水線與MLflow跟蹤4.1 端到端機器學習流水線4.2 MLflow跟蹤實驗…

英語學習:讀科技論文的難處

如果讀起科技論文&#xff0c; 我們就知道自己到底欠缺什么知識了&#xff0c; 那是一個挨著一個的缺。 而且還沒有維基百科可用。 怎么辦&#xff1f;沒辦法&#xff01;硬看&#xff01; 而且還要面臨語言的差異性困難。比如這一句怎么翻譯比較合適&#xff1f;還是直接不翻譯…