日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > java >内容正文

java

Java NIO之Channel(通道)

發(fā)布時(shí)間:2025/7/14 java 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java NIO之Channel(通道) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

**Java高級(jí)特性增強(qiáng)-NIO

本部分網(wǎng)絡(luò)上有大量的資源可以參考,在這里做了部分整理并做了部分勘誤,感謝前輩的付出,每節(jié)文章末尾有引用列表~


寫(xiě)在所有文字的前面:作者在此特別推薦Google排名第一的關(guān)于NIO的文章: tutorials.jenkov.com/java-nio/in… 雖然是英文的,但是看下來(lái)并不困難。后面如果各位看官呼聲很高,作者會(huì)翻譯這一系列文章。

請(qǐng)戳GitHub原文: github.com/wangzhiwubi…

大數(shù)據(jù)成神之路系列:

請(qǐng)戳GitHub原文: github.com/wangzhiwubi…

Java高級(jí)特性增強(qiáng)-集合

Java高級(jí)特性增強(qiáng)-多線程

Java高級(jí)特性增強(qiáng)-Synchronized

Java高級(jí)特性增強(qiáng)-volatile

Java高級(jí)特性增強(qiáng)-并發(fā)集合框架

Java高級(jí)特性增強(qiáng)-分布式

Java高級(jí)特性增強(qiáng)-Zookeeper

Java高級(jí)特性增強(qiáng)-JVM

Java高級(jí)特性增強(qiáng)-NIO

Java NIO之Channel(通道)

Buffer(緩沖區(qū))介紹

通常來(lái)說(shuō)NIO中的所有IO都是從 Channel(通道) 開(kāi)始的。

  • 從通道進(jìn)行數(shù)據(jù)讀取 :創(chuàng)建一個(gè)緩沖區(qū),然后請(qǐng)求通道讀取數(shù)據(jù)。
  • 從通道進(jìn)行數(shù)據(jù)寫(xiě)入 :創(chuàng)建一個(gè)緩沖區(qū),填充數(shù)據(jù),并要求通道寫(xiě)入數(shù)據(jù)。

數(shù)據(jù)讀取和寫(xiě)入操作圖示: ![342194a2fdfeaf96e6051e08c9951de3](Java NIO之Channel(通道).resources/2958433B-EEAF-4D8B-98A2-39941C7C1733.png)

Java NIO Channel通道和流非常相似,主要有以下幾點(diǎn)區(qū)別:

通道可以讀也可以寫(xiě),流一般來(lái)說(shuō)是單向的(只能讀或者寫(xiě),所以之前我們用流進(jìn)行IO操作的時(shí)候需要分別創(chuàng)建一個(gè)輸入流和一個(gè)輸出流)。 通道可以異步讀寫(xiě)。 通道總是基于緩沖區(qū)Buffer來(lái)讀寫(xiě)。

Java NIO中最重要的幾個(gè)Channel的實(shí)現(xiàn):

  • FileChannel: 用于文件的數(shù)據(jù)讀寫(xiě)
  • DatagramChannel: 用于UDP的數(shù)據(jù)讀寫(xiě)
  • SocketChannel: 用于TCP的數(shù)據(jù)讀寫(xiě),一般是客戶(hù)端實(shí)現(xiàn)
  • ServerSocketChannel: 允許我們監(jiān)聽(tīng)TCP鏈接請(qǐng)求,每個(gè)請(qǐng)求會(huì)創(chuàng)建會(huì)一個(gè)SocketChannel,一般是服務(wù)器實(shí)現(xiàn)

類(lèi)層次結(jié)構(gòu): 下面的UML圖使用Idea生成的。 ![5153431ea4cfbf8d64f746d098f8bda5](Java NIO之Channel(通道).resources/3A2E73E4-2445-4B90-93F0-0EB34EB8C82B.png)

FileChannel的使用

使用FileChannel讀取數(shù)據(jù)到Buffer(緩沖區(qū))以及利用Buffer(緩沖區(qū))寫(xiě)入數(shù)據(jù)到FileChannel:

package filechannel;import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel;public class FileChannelTxt {public static void main(String args[]) throws IOException {//1.創(chuàng)建一個(gè)RandomAccessFile(隨機(jī)訪問(wèn)文件)對(duì)象,RandomAccessFile raf=new RandomAccessFile("D:\\niodata.txt", "rw");//通過(guò)RandomAccessFile對(duì)象的getChannel()方法。FileChannel是抽象類(lèi)。FileChannel inChannel=raf.getChannel();//2.創(chuàng)建一個(gè)讀數(shù)據(jù)緩沖區(qū)對(duì)象ByteBuffer buf=ByteBuffer.allocate(48);//3.從通道中讀取數(shù)據(jù)int bytesRead = inChannel.read(buf);//創(chuàng)建一個(gè)寫(xiě)數(shù)據(jù)緩沖區(qū)對(duì)象ByteBuffer buf2=ByteBuffer.allocate(48);//寫(xiě)入數(shù)據(jù)buf2.put("filechannel test".getBytes());buf2.flip();inChannel.write(buf);while (bytesRead != -1) {System.out.println("Read " + bytesRead);//Buffer有兩種模式,寫(xiě)模式和讀模式。在寫(xiě)模式下調(diào)用flip()之后,Buffer從寫(xiě)模式變成讀模式。buf.flip();//如果還有未讀內(nèi)容while (buf.hasRemaining()) {System.out.print((char) buf.get());}//清空緩存區(qū)buf.clear();bytesRead = inChannel.read(buf);}//關(guān)閉RandomAccessFile(隨機(jī)訪問(wèn)文件)對(duì)象raf.close();} }復(fù)制代碼

運(yùn)行效果: ![93e3d051206ec5c22f1997fae7e3a143](Java NIO之Channel(通道).resources/0CC9E605-79FB-455E-AF3F-1CD41832B4A6.png) 通過(guò)上述實(shí)例代碼,我們可以大概總結(jié)出FileChannel的一般使用規(guī)則:

1. 開(kāi)啟FileChannel

使用之前,FileChannel必須被打開(kāi) ,但是你無(wú)法直接打開(kāi)FileChannel(FileChannel是抽象類(lèi))。需要通過(guò) InputStream , OutputStream 或 RandomAccessFile 獲取FileChannel。 我們上面的例子是通過(guò)RandomAccessFile打開(kāi)FileChannel的:

//1.創(chuàng)建一個(gè)RandomAccessFile(隨機(jī)訪問(wèn)文件)對(duì)象,RandomAccessFile raf=new RandomAccessFile("D:\\niodata.txt", "rw");//通過(guò)RandomAccessFile對(duì)象的getChannel()方法。FileChannel是抽象類(lèi)。FileChannel inChannel=raf.getChannel(); 復(fù)制代碼

2. 從FileChannel讀取數(shù)據(jù)/寫(xiě)入數(shù)據(jù) 從FileChannel中讀取數(shù)據(jù)/寫(xiě)入數(shù)據(jù)之前首先要?jiǎng)?chuàng)建一個(gè)Buffer(緩沖區(qū))對(duì)象,Buffer(緩沖區(qū))對(duì)象的使用我們?cè)谏弦黄恼轮幸呀?jīng)詳細(xì)說(shuō)明了,如果不了解的話可以看我的上一篇關(guān)于Buffer的文章。

使用FileChannel的read()方法讀取數(shù)據(jù):

//2.創(chuàng)建一個(gè)讀數(shù)據(jù)緩沖區(qū)對(duì)象ByteBuffer buf=ByteBuffer.allocate(48); //3.從通道中讀取數(shù)據(jù)int bytesRead = inChannel.read(buf); 復(fù)制代碼

使用FileChannel的write()方法寫(xiě)入數(shù)據(jù):

//創(chuàng)建一個(gè)寫(xiě)數(shù)據(jù)緩沖區(qū)對(duì)象ByteBuffer buf2=ByteBuffer.allocate(48);//寫(xiě)入數(shù)據(jù)buf2.put("filechannel test".getBytes());buf2.flip();inChannel.write(buf); 復(fù)制代碼

3. 關(guān)閉FileChannel

完成使用后,FileChannel您必須關(guān)閉它。

channel.close(); 復(fù)制代碼

SocketChannel和ServerSocketChannel的使用

利用SocketChannel和ServerSocketChannel實(shí)現(xiàn)客戶(hù)端與服務(wù)器端簡(jiǎn)單通信: SocketChannel 用于創(chuàng)建基于tcp協(xié)議的客戶(hù)端對(duì)象,因?yàn)镾ocketChannel中不存在accept()方法,所以,它不能成為一個(gè)服務(wù)端程序。通過(guò) connect()方法 ,SocketChannel對(duì)象可以連接到其他tcp服務(wù)器程序。 客戶(hù)端:

package socketchannel;import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel;public class WebClient {public static void main(String[] args) throws IOException {//1.通過(guò)SocketChannel的open()方法創(chuàng)建一個(gè)SocketChannel對(duì)象SocketChannel socketChannel = SocketChannel.open();//2.連接到遠(yuǎn)程服務(wù)器(連接此通道的socket)socketChannel.connect(new InetSocketAddress("127.0.0.1", 3333));// 3.創(chuàng)建寫(xiě)數(shù)據(jù)緩存區(qū)對(duì)象ByteBuffer writeBuffer = ByteBuffer.allocate(128);writeBuffer.put("hello WebServer this is from WebClient".getBytes());writeBuffer.flip();socketChannel.write(writeBuffer);//創(chuàng)建讀數(shù)據(jù)緩存區(qū)對(duì)象ByteBuffer readBuffer = ByteBuffer.allocate(128);socketChannel.read(readBuffer);//String 字符串常量,不可變;StringBuffer 字符串變量(線程安全),可變;StringBuilder 字符串變量(非線程安全),可變StringBuilder stringBuffer=new StringBuilder();//4.將Buffer從寫(xiě)模式變?yōu)榭勺x模式readBuffer.flip();while (readBuffer.hasRemaining()) {stringBuffer.append((char) readBuffer.get());}System.out.println("從服務(wù)端接收到的數(shù)據(jù):"+stringBuffer);socketChannel.close();}} 復(fù)制代碼

ServerSocketChannel 允許我們監(jiān)聽(tīng)TCP鏈接請(qǐng)求,通過(guò)ServerSocketChannelImpl的 accept()方法 可以創(chuàng)建一個(gè)SocketChannel對(duì)象用戶(hù)從客戶(hù)端讀/寫(xiě)數(shù)據(jù)。

服務(wù)端:

package socketchannel;import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel;public class WebServer {public static void main(String args[]) throws IOException {try {//1.通過(guò)ServerSocketChannel 的open()方法創(chuàng)建一個(gè)ServerSocketChannel對(duì)象,open方法的作用:打開(kāi)套接字通道ServerSocketChannel ssc = ServerSocketChannel.open();//2.通過(guò)ServerSocketChannel綁定ip地址和port(端口號(hào))ssc.socket().bind(new InetSocketAddress("127.0.0.1", 3333));//通過(guò)ServerSocketChannelImpl的accept()方法創(chuàng)建一個(gè)SocketChannel對(duì)象用戶(hù)從客戶(hù)端讀/寫(xiě)數(shù)據(jù)SocketChannel socketChannel = ssc.accept();//3.創(chuàng)建寫(xiě)數(shù)據(jù)的緩存區(qū)對(duì)象ByteBuffer writeBuffer = ByteBuffer.allocate(128);writeBuffer.put("hello WebClient this is from WebServer".getBytes());writeBuffer.flip();socketChannel.write(writeBuffer);//創(chuàng)建讀數(shù)據(jù)的緩存區(qū)對(duì)象ByteBuffer readBuffer = ByteBuffer.allocate(128);//讀取緩存區(qū)數(shù)據(jù)socketChannel.read(readBuffer);StringBuilder stringBuffer=new StringBuilder();//4.將Buffer從寫(xiě)模式變?yōu)榭勺x模式readBuffer.flip();while (readBuffer.hasRemaining()) {stringBuffer.append((char) readBuffer.get());}System.out.println("從客戶(hù)端接收到的數(shù)據(jù):"+stringBuffer);socketChannel.close();ssc.close();} catch (IOException e) {e.printStackTrace();}} } 復(fù)制代碼

運(yùn)行效果 客戶(hù)端: ![37ac5661df301bcc55f3bab690d6c3ea](Java NIO之Channel(通道).resources/6AF85EF7-83C7-48B6-A6AB-C70AD22A91D4.png) 服務(wù)端: ![d6b8298bd2108e3fcd6ed422cec8daa8](Java NIO之Channel(通道).resources/821A61BD-80DF-493F-99D8-4F5330211339.png) 通過(guò)上述實(shí)例代碼,我們可以大概總結(jié)出SocketChannel和ServerSocketChannel的使用的一般使用規(guī)則: 考慮到篇幅問(wèn)題,下面只給出大致步驟,不貼代碼,可以結(jié)合上述實(shí)例理解。 客戶(hù)端 1.通過(guò)SocketChannel連接到遠(yuǎn)程服務(wù)器 2.創(chuàng)建讀數(shù)據(jù)/寫(xiě)數(shù)據(jù)緩沖區(qū)對(duì)象來(lái)讀取服務(wù)端數(shù)據(jù)或向服務(wù)端發(fā)送數(shù)據(jù) 3.關(guān)閉SocketChannel 服務(wù)端 1.通過(guò)ServerSocketChannel 綁定ip地址和端口號(hào) 2.通過(guò)ServerSocketChannelImpl的accept()方法創(chuàng)建一個(gè)SocketChannel對(duì)象用戶(hù)從客戶(hù)端讀/寫(xiě)數(shù)據(jù) 3.創(chuàng)建讀數(shù)據(jù)/寫(xiě)數(shù)據(jù)緩沖區(qū)對(duì)象來(lái)讀取客戶(hù)端數(shù)據(jù)或向客戶(hù)端發(fā)送數(shù)據(jù) 4. 關(guān)閉SocketChannel和ServerSocketChannel

DatagramChannel的使用

DataGramChannel,類(lèi)似于java 網(wǎng)絡(luò)編程的DatagramSocket類(lèi);使用UDP進(jìn)行網(wǎng)絡(luò)傳輸, UDP是無(wú)連接,面向數(shù)據(jù)報(bào)文段的協(xié)議,對(duì)傳輸?shù)臄?shù)據(jù)不保證安全與完整 ;和上面介紹的SocketChannel和ServerSocketChannel的使用方法類(lèi)似,所以這里就簡(jiǎn)單介紹一下如何使用。 1.獲取DataGramChannel

//1.通過(guò)DatagramChannel的open()方法創(chuàng)建一個(gè)DatagramChannel對(duì)象DatagramChannel datagramChannel = DatagramChannel.open();//綁定一個(gè)port(端口)datagramChannel.bind(new InetSocketAddress(1234)); 復(fù)制代碼

上面代碼表示程序可以在1234端口接收數(shù)據(jù)報(bào)。

2.接收/發(fā)送消息 接收消息: 先創(chuàng)建一個(gè)緩存區(qū)對(duì)象,然后通過(guò)receive方法接收消息,這個(gè)方法返回一個(gè)SocketAddress對(duì)象,表示發(fā)送消息方的地址:

ByteBuffer buf = ByteBuffer.allocate(48); buf.clear(); channel.receive(buf); 復(fù)制代碼

發(fā)送消息: 由于UDP下,服務(wù)端和客戶(hù)端通信并不需要建立連接,只需要知道對(duì)方地址即可發(fā)出消息,但是是否發(fā)送成功或者成功被接收到是沒(méi)有保證的;發(fā)送消息通過(guò)send方法發(fā)出,改方法返回一個(gè)int值,表示成功發(fā)送的字節(jié)數(shù):

ByteBuffer buf = ByteBuffer.allocate(48); buf.clear(); buf.put("datagramchannel".getBytes()); buf.flip(); int send = channel.send(buffer, new InetSocketAddress("localhost",1234)); 復(fù)制代碼

這個(gè)例子發(fā)送一串字符:“datagramchannel”到主機(jī)名為”localhost”服務(wù)器的端口1234上。

Scatter/Gather

Channel 提供了一種被稱(chēng)為 Scatter/Gather 的新功能,也稱(chēng)為本地矢量 I/O。Scatter/Gather 是指在多個(gè)緩沖區(qū)上實(shí)現(xiàn)一個(gè)簡(jiǎn)單的 I/O 操作。正確使用 Scatter / Gather可以明顯提高性能。 大多數(shù)現(xiàn)代操作系統(tǒng)都支持本地矢量I/O(native vectored I/O)操作。當(dāng)您在一個(gè)通道上請(qǐng)求一個(gè)Scatter/Gather操作時(shí),該請(qǐng)求會(huì)被翻譯為適當(dāng)?shù)谋镜卣{(diào)用來(lái)直接填充或抽取緩沖區(qū),減少或避免了緩沖區(qū)拷貝和系統(tǒng)調(diào)用; Scatter/Gather應(yīng)該使用直接的ByteBuffers以從本地I/O獲取最大性能優(yōu)勢(shì)。 Scatter/Gather功能是通道(Channel)提供的 并不是Buffer。

  • Scatter: 從一個(gè)Channel讀取的信息分散到N個(gè)緩沖區(qū)中(Buufer).
  • Gather: 將N個(gè)Buffer里面內(nèi)容按照順序發(fā)送到一個(gè)Channel.

Scattering Reads "scattering read"是把數(shù)據(jù)從單個(gè)Channel寫(xiě)入到多個(gè)buffer,如下圖所示: ![820b8ed4fd205e451772c9d18e0d629f](Java NIO之Channel(通道).resources/D2633F82-0A59-488A-AEC6-AB443A3125F4.png) 示例代碼:

ByteBuffer header = ByteBuffer.allocate(128); ByteBuffer body = ByteBuffer.allocate(1024); ByteBuffer[] bufferArray = { header, body }; channel.read(bufferArray); 復(fù)制代碼

read()方法內(nèi)部會(huì)負(fù)責(zé)把數(shù)據(jù)按順序?qū)戇M(jìn)傳入的buffer數(shù)組內(nèi)。一個(gè)buffer寫(xiě)滿(mǎn)后,接著寫(xiě)到下一個(gè)buffer中。 舉個(gè)例子,假如通道中有200個(gè)字節(jié)數(shù)據(jù),那么header會(huì)被寫(xiě)入128個(gè)字節(jié)數(shù)據(jù),body會(huì)被寫(xiě)入72個(gè)字節(jié)數(shù)據(jù); 注意: 無(wú)論是scatter還是gather操作,都是按照buffer在數(shù)組中的順序來(lái)依次讀取或?qū)懭氲?#xff1b; Gathering Writes "gathering write"把多個(gè)buffer的數(shù)據(jù)寫(xiě)入到同一個(gè)channel中,下面是示意圖 ![f39ff57a4463a05cc93ae22f402e6683](Java NIO之Channel(通道).resources/19060EA5-78B2-49F1-A706-0C99F3BC51A5.png) 示例代碼:

ByteBuffer header = ByteBuffer.allocate(128); ByteBuffer body = ByteBuffer.allocate(1024); //write data into buffers ByteBuffer[] bufferArray = { header, body }; channel.write(bufferArray); 復(fù)制代碼

write()方法內(nèi)部會(huì)負(fù)責(zé)把數(shù)據(jù)按順序?qū)懭氲絚hannel中。 注意: 并不是所有數(shù)據(jù)都寫(xiě)入到通道,寫(xiě)入的數(shù)據(jù)要根據(jù)position和limit的值來(lái)判斷,只有position和limit之間的數(shù)據(jù)才會(huì)被寫(xiě)入; 舉個(gè)例子,假如以上header緩沖區(qū)中有128個(gè)字節(jié)數(shù)據(jù),但此時(shí)position=0,limit=58;那么只有下標(biāo)索引為0-57的數(shù)據(jù)才會(huì)被寫(xiě)入到通道中.

通道之間的數(shù)據(jù)傳輸

在Java NIO中如果一個(gè)channel是FileChannel類(lèi)型的,那么他可以直接把數(shù)據(jù)傳輸?shù)搅硪粋€(gè)channel。

  • transferFrom(): transferFrom方法把數(shù)據(jù)從通道源傳輸?shù)紽ileChannel
  • transferTo(): transferTo方法把FileChannel數(shù)據(jù)傳輸?shù)搅硪粋€(gè)channel

參考文檔:

  • 官方JDK相關(guān)文檔
  • 谷歌搜索排名第一的Java NIO教程
  • 《Java程序員修煉之道》
  • ByteBuffer常用方法詳解
  • JavaNIO易百教程

參考文章: 《Netty官網(wǎng)》

www.jianshu.com/nb/18340870

請(qǐng)戳GitHub原文: https://github.com/wangzhiwubigdata/God-Of-BigData關(guān)注公眾號(hào),內(nèi)推,面試,資源下載,關(guān)注更多大數(shù)據(jù)技術(shù)~大數(shù)據(jù)成神之路~預(yù)計(jì)更新500+篇文章,已經(jīng)更新50+篇~ 復(fù)制代碼

總結(jié)

以上是生活随笔為你收集整理的Java NIO之Channel(通道)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。