日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

Java NIO之Channel(通道)

發布時間:2025/7/14 63 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java NIO之Channel(通道) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

**Java高級特性增強-NIO

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


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

請戳GitHub原文: github.com/wangzhiwubi…

大數據成神之路系列:

請戳GitHub原文: github.com/wangzhiwubi…

Java高級特性增強-集合

Java高級特性增強-多線程

Java高級特性增強-Synchronized

Java高級特性增強-volatile

Java高級特性增強-并發集合框架

Java高級特性增強-分布式

Java高級特性增強-Zookeeper

Java高級特性增強-JVM

Java高級特性增強-NIO

Java NIO之Channel(通道)

Buffer(緩沖區)介紹

通常來說NIO中的所有IO都是從 Channel(通道) 開始的。

  • 從通道進行數據讀取 :創建一個緩沖區,然后請求通道讀取數據。
  • 從通道進行數據寫入 :創建一個緩沖區,填充數據,并要求通道寫入數據。

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

Java NIO Channel通道和流非常相似,主要有以下幾點區別:

通道可以讀也可以寫,流一般來說是單向的(只能讀或者寫,所以之前我們用流進行IO操作的時候需要分別創建一個輸入流和一個輸出流)。 通道可以異步讀寫。 通道總是基于緩沖區Buffer來讀寫。

Java NIO中最重要的幾個Channel的實現:

  • FileChannel: 用于文件的數據讀寫
  • DatagramChannel: 用于UDP的數據讀寫
  • SocketChannel: 用于TCP的數據讀寫,一般是客戶端實現
  • ServerSocketChannel: 允許我們監聽TCP鏈接請求,每個請求會創建會一個SocketChannel,一般是服務器實現

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

FileChannel的使用

使用FileChannel讀取數據到Buffer(緩沖區)以及利用Buffer(緩沖區)寫入數據到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.創建一個RandomAccessFile(隨機訪問文件)對象,RandomAccessFile raf=new RandomAccessFile("D:\\niodata.txt", "rw");//通過RandomAccessFile對象的getChannel()方法。FileChannel是抽象類。FileChannel inChannel=raf.getChannel();//2.創建一個讀數據緩沖區對象ByteBuffer buf=ByteBuffer.allocate(48);//3.從通道中讀取數據int bytesRead = inChannel.read(buf);//創建一個寫數據緩沖區對象ByteBuffer buf2=ByteBuffer.allocate(48);//寫入數據buf2.put("filechannel test".getBytes());buf2.flip();inChannel.write(buf);while (bytesRead != -1) {System.out.println("Read " + bytesRead);//Buffer有兩種模式,寫模式和讀模式。在寫模式下調用flip()之后,Buffer從寫模式變成讀模式。buf.flip();//如果還有未讀內容while (buf.hasRemaining()) {System.out.print((char) buf.get());}//清空緩存區buf.clear();bytesRead = inChannel.read(buf);}//關閉RandomAccessFile(隨機訪問文件)對象raf.close();} }復制代碼

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

1. 開啟FileChannel

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

//1.創建一個RandomAccessFile(隨機訪問文件)對象,RandomAccessFile raf=new RandomAccessFile("D:\\niodata.txt", "rw");//通過RandomAccessFile對象的getChannel()方法。FileChannel是抽象類。FileChannel inChannel=raf.getChannel(); 復制代碼

2. 從FileChannel讀取數據/寫入數據 從FileChannel中讀取數據/寫入數據之前首先要創建一個Buffer(緩沖區)對象,Buffer(緩沖區)對象的使用我們在上一篇文章中已經詳細說明了,如果不了解的話可以看我的上一篇關于Buffer的文章。

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

//2.創建一個讀數據緩沖區對象ByteBuffer buf=ByteBuffer.allocate(48); //3.從通道中讀取數據int bytesRead = inChannel.read(buf); 復制代碼

使用FileChannel的write()方法寫入數據:

//創建一個寫數據緩沖區對象ByteBuffer buf2=ByteBuffer.allocate(48);//寫入數據buf2.put("filechannel test".getBytes());buf2.flip();inChannel.write(buf); 復制代碼

3. 關閉FileChannel

完成使用后,FileChannel您必須關閉它。

channel.close(); 復制代碼

SocketChannel和ServerSocketChannel的使用

利用SocketChannel和ServerSocketChannel實現客戶端與服務器端簡單通信: SocketChannel 用于創建基于tcp協議的客戶端對象,因為SocketChannel中不存在accept()方法,所以,它不能成為一個服務端程序。通過 connect()方法 ,SocketChannel對象可以連接到其他tcp服務器程序。 客戶端:

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.通過SocketChannel的open()方法創建一個SocketChannel對象SocketChannel socketChannel = SocketChannel.open();//2.連接到遠程服務器(連接此通道的socket)socketChannel.connect(new InetSocketAddress("127.0.0.1", 3333));// 3.創建寫數據緩存區對象ByteBuffer writeBuffer = ByteBuffer.allocate(128);writeBuffer.put("hello WebServer this is from WebClient".getBytes());writeBuffer.flip();socketChannel.write(writeBuffer);//創建讀數據緩存區對象ByteBuffer readBuffer = ByteBuffer.allocate(128);socketChannel.read(readBuffer);//String 字符串常量,不可變;StringBuffer 字符串變量(線程安全),可變;StringBuilder 字符串變量(非線程安全),可變StringBuilder stringBuffer=new StringBuilder();//4.將Buffer從寫模式變為可讀模式readBuffer.flip();while (readBuffer.hasRemaining()) {stringBuffer.append((char) readBuffer.get());}System.out.println("從服務端接收到的數據:"+stringBuffer);socketChannel.close();}} 復制代碼

ServerSocketChannel 允許我們監聽TCP鏈接請求,通過ServerSocketChannelImpl的 accept()方法 可以創建一個SocketChannel對象用戶從客戶端讀/寫數據。

服務端:

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.通過ServerSocketChannel 的open()方法創建一個ServerSocketChannel對象,open方法的作用:打開套接字通道ServerSocketChannel ssc = ServerSocketChannel.open();//2.通過ServerSocketChannel綁定ip地址和port(端口號)ssc.socket().bind(new InetSocketAddress("127.0.0.1", 3333));//通過ServerSocketChannelImpl的accept()方法創建一個SocketChannel對象用戶從客戶端讀/寫數據SocketChannel socketChannel = ssc.accept();//3.創建寫數據的緩存區對象ByteBuffer writeBuffer = ByteBuffer.allocate(128);writeBuffer.put("hello WebClient this is from WebServer".getBytes());writeBuffer.flip();socketChannel.write(writeBuffer);//創建讀數據的緩存區對象ByteBuffer readBuffer = ByteBuffer.allocate(128);//讀取緩存區數據socketChannel.read(readBuffer);StringBuilder stringBuffer=new StringBuilder();//4.將Buffer從寫模式變為可讀模式readBuffer.flip();while (readBuffer.hasRemaining()) {stringBuffer.append((char) readBuffer.get());}System.out.println("從客戶端接收到的數據:"+stringBuffer);socketChannel.close();ssc.close();} catch (IOException e) {e.printStackTrace();}} } 復制代碼

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

DatagramChannel的使用

DataGramChannel,類似于java 網絡編程的DatagramSocket類;使用UDP進行網絡傳輸, UDP是無連接,面向數據報文段的協議,對傳輸的數據不保證安全與完整 ;和上面介紹的SocketChannel和ServerSocketChannel的使用方法類似,所以這里就簡單介紹一下如何使用。 1.獲取DataGramChannel

//1.通過DatagramChannel的open()方法創建一個DatagramChannel對象DatagramChannel datagramChannel = DatagramChannel.open();//綁定一個port(端口)datagramChannel.bind(new InetSocketAddress(1234)); 復制代碼

上面代碼表示程序可以在1234端口接收數據報。

2.接收/發送消息 接收消息: 先創建一個緩存區對象,然后通過receive方法接收消息,這個方法返回一個SocketAddress對象,表示發送消息方的地址:

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

發送消息: 由于UDP下,服務端和客戶端通信并不需要建立連接,只需要知道對方地址即可發出消息,但是是否發送成功或者成功被接收到是沒有保證的;發送消息通過send方法發出,改方法返回一個int值,表示成功發送的字節數:

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

這個例子發送一串字符:“datagramchannel”到主機名為”localhost”服務器的端口1234上。

Scatter/Gather

Channel 提供了一種被稱為 Scatter/Gather 的新功能,也稱為本地矢量 I/O。Scatter/Gather 是指在多個緩沖區上實現一個簡單的 I/O 操作。正確使用 Scatter / Gather可以明顯提高性能。 大多數現代操作系統都支持本地矢量I/O(native vectored I/O)操作。當您在一個通道上請求一個Scatter/Gather操作時,該請求會被翻譯為適當的本地調用來直接填充或抽取緩沖區,減少或避免了緩沖區拷貝和系統調用; Scatter/Gather應該使用直接的ByteBuffers以從本地I/O獲取最大性能優勢。 Scatter/Gather功能是通道(Channel)提供的 并不是Buffer。

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

Scattering Reads "scattering read"是把數據從單個Channel寫入到多個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); 復制代碼

read()方法內部會負責把數據按順序寫進傳入的buffer數組內。一個buffer寫滿后,接著寫到下一個buffer中。 舉個例子,假如通道中有200個字節數據,那么header會被寫入128個字節數據,body會被寫入72個字節數據; 注意: 無論是scatter還是gather操作,都是按照buffer在數組中的順序來依次讀取或寫入的; Gathering Writes "gathering write"把多個buffer的數據寫入到同一個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); 復制代碼

write()方法內部會負責把數據按順序寫入到channel中。 注意: 并不是所有數據都寫入到通道,寫入的數據要根據position和limit的值來判斷,只有position和limit之間的數據才會被寫入; 舉個例子,假如以上header緩沖區中有128個字節數據,但此時position=0,limit=58;那么只有下標索引為0-57的數據才會被寫入到通道中.

通道之間的數據傳輸

在Java NIO中如果一個channel是FileChannel類型的,那么他可以直接把數據傳輸到另一個channel。

  • transferFrom(): transferFrom方法把數據從通道源傳輸到FileChannel
  • transferTo(): transferTo方法把FileChannel數據傳輸到另一個channel

參考文檔:

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

參考文章: 《Netty官網》

www.jianshu.com/nb/18340870

請戳GitHub原文: https://github.com/wangzhiwubigdata/God-Of-BigData關注公眾號,內推,面試,資源下載,關注更多大數據技術~大數據成神之路~預計更新500+篇文章,已經更新50+篇~ 復制代碼

總結

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

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。