(3)NIO 之 Channel(通道)

FileChannel的使用

SocketChannel和ServerSocketChannel的使用

?DatagramChannel的使用

Scatter / Gather

????Scatter: 從Channel讀取的信息分散到N個緩沖區(qū)中(Buufer).

????Gather: 將N個Buffer里面內(nèi)容按照順序發(fā)送到一個Channel.

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

一 Channel(通道)介紹

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

1、和流區(qū)別:可讀可寫,可異步;基于緩沖區(qū)Buffer;單向

2、重要實(shí)現(xiàn):

FileChannel?文件讀寫

DatagramChannel:UDP數(shù)據(jù)讀寫

SocketChannel:TCP的數(shù)據(jù)讀寫,客戶端實(shí)現(xiàn)

ServerSocketChannel:允許監(jiān)聽TCP鏈接請求,每個請求會創(chuàng)建會一個SocketChannel,服務(wù)器實(shí)現(xiàn)


二 FileChannel的使用

1. 開啟FileChannel

必須被打開,無法直接打開抽象類。通過InputStreamOutputStreamRandomAccessFile獲取FileChannel

//1.創(chuàng)建一個RandomAccessFile(隨機(jī)訪問文件)對象,

RandomAccessFile raf=new RandomAccessFile("D:\\niodata.txt", "rw");

//通過RandomAccessFile對象的getChannel()方法。FileChannel是抽象類。

FileChannel inChannel=raf.getChannel();

2. 從FileChannel讀取數(shù)據(jù)/寫入數(shù)據(jù)

創(chuàng)建一個Buffer(緩沖區(qū))對象

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

//2.創(chuàng)建一個讀數(shù)據(jù)緩沖區(qū)對象

ByteBuffer buf=ByteBuffer.allocate(48);

//3.從通道中讀取數(shù)據(jù)

int bytesRead = inChannel.read(buf);

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

? ? ? ?//創(chuàng)建一個寫數(shù)據(jù)緩沖區(qū)對象

? ? ? ?ByteBuffer buf2=ByteBuffer.allocate(48);

? ? ? ?//寫入數(shù)據(jù)

? ? ? ?buf2.put("filechannel test".getBytes());

? ? ? ?buf2.flip();

? ? ? ?inChannel.write(buf);

3. 關(guān)閉FileChannel:channel.close();? ??

三 SocketChannel和ServerSocketChannel的使用

利用SocketChannel和ServerSocketChannel實(shí)現(xiàn)客戶端與服務(wù)器端簡單通信:

SocketChannel用于創(chuàng)建基于tcp協(xié)議的客戶端對象,因?yàn)镾ocketChannel中不存在accept()方法,所以,它不能成為一個服務(wù)端程序。通過connect()方法,SocketChannel對象可以連接到其他tcp服務(wù)器程序。

客戶端:

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

服務(wù)端:

運(yùn)行效果:

通過上述實(shí)例代碼,我們可以大概總結(jié)出SocketChannel和ServerSocketChannel的使用的一般使用規(guī)則:

考慮到篇幅問題,下面只給出大致步驟,不貼代碼,可以結(jié)合上述實(shí)例理解。

客戶端

1.通過SocketChannel連接遠(yuǎn)程服務(wù)器

2.創(chuàng)建數(shù)據(jù)/寫數(shù)據(jù)緩沖區(qū)對象讀取服務(wù)端數(shù)據(jù)或向服務(wù)端發(fā)送數(shù)據(jù)

3.關(guān)閉SocketChannel

服務(wù)端

1.通過ServerSocketChannel 綁定ip地址和端口號

2.通過ServerSocketChannelImpl的accept()方法創(chuàng)建一個SocketChannel對象用戶從客戶端讀/寫數(shù)據(jù)

3.創(chuàng)建讀數(shù)據(jù)/寫數(shù)據(jù)緩沖區(qū)對象來讀取客戶端數(shù)據(jù)或向客戶端發(fā)送數(shù)據(jù)

4. 關(guān)閉SocketChannel和ServerSocketChannel

四 ?DatagramChannel的使用

類似DatagramSocket類;UDP網(wǎng)絡(luò)傳輸無連接,面向數(shù)據(jù)報文段的協(xié)議,不保證安全與完整;和SocketChannel和ServerSocketChannel使用方法類似

1.獲取DataGramChannel

? ? ? ?//1.通過DatagramChannel的open()方法創(chuàng)建一個DatagramChannel對象

? ? ? ?DatagramChannel datagramChannel = DatagramChannel.open();

? ? ? ?//綁定一個port(端口)

? ? ? ?datagramChannel.bind(new InetSocketAddress(1234));

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

2.接收/發(fā)送消息

接收消息:

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

ByteBuffer buf = ByteBuffer.allocate(48);

buf.clear();

channel.receive(buf);

發(fā)送消息:

由于UDP下,服務(wù)端和客戶端通信并不需要建立連接,只需要知道對方地址即可發(fā)出消息,但是是否發(fā)送成功或者成功被接收到是沒有保證的;發(fā)送消息通過send方法發(fā)出,改方法返回一個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ā)送一串字符:“datagramchannel”到主機(jī)名為”localhost”服務(wù)器的端口1234上。

五 Scatter / Gather(散射/采集)

Channel 提供了一種被稱為 Scatter/Gather 的新功能,也稱為本地矢量 I/O。Scatter/Gather 是指在多個緩沖區(qū)上實(shí)現(xiàn)一個簡單的 I/O 操作。正確使用 Scatter / Gather可以明顯提高性能

大多數(shù)現(xiàn)代操作系統(tǒng)都支持本地矢量I/O(native vectored I/O)操作。當(dāng)您在一個通道上請求一個Scatter/Gather操作時,該請求會被翻譯為適當(dāng)?shù)?b>本地調(diào)用來直接填充或抽取緩沖區(qū)減少或避免了緩沖區(qū)拷貝和系統(tǒng)調(diào)用

Scatter/Gather應(yīng)該使用直接ByteBuffers以從本地I/O獲取最大性能優(yōu)勢

Scatter/Gather功能是通道(Channel)提供的 并不是Buffer。

Scatter:從一個Channel讀取的信息分散到N個緩沖區(qū)中(Buufer).

Gather:將N個Buffer里面內(nèi)容按照順序發(fā)送到一個Channel.

Scattering Reads

"scattering read"是把數(shù)據(jù)從單個Channel寫入到多個buffer,如下圖所示:


ByteBuffer header = ByteBuffer.allocate(128);

ByteBuffer body ? = ByteBuffer.allocate(1024);

ByteBuffer[] bufferArray = { header, body };

channel.read(bufferArray);

read()方法內(nèi)部會負(fù)責(zé)把數(shù)據(jù)按順序?qū)戇M(jìn)傳入的buffer數(shù)組內(nèi)。一個buffer寫滿后,接著寫到下一個buffer中。

舉個例子,假如通道中有200個字節(jié)數(shù)據(jù),那么header會被寫入128個字節(jié)數(shù)據(jù),body會被寫入72個字節(jié)數(shù)據(jù);

注意:

無論是scatter還是gather操作,都是按照buffer在數(shù)組中的順序來依次讀取或?qū)懭氲模?/p>

Gathering Writes

"gathering write"把多個buffer的數(shù)據(jù)寫入到同一個channel中,下面是示意圖:

示例代碼:

ByteBuffer header = ByteBuffer.allocate(128);

ByteBuffer body ? = ByteBuffer.allocate(1024);

//write data into buffers

ByteBuffer[] bufferArray = { header, body };

channel.write(bufferArray);

write()方法內(nèi)部會負(fù)責(zé)把數(shù)據(jù)按順序?qū)懭氲絚hannel中。

注意:

并不是所有數(shù)據(jù)都寫入到通道,寫入的數(shù)據(jù)要根據(jù)position和limit的值來判斷,只有position和limit之間的數(shù)據(jù)才會被寫入

舉個例子,假如以上header緩沖區(qū)中有128個字節(jié)數(shù)據(jù),但此時position=0,limit=58;那么只有下標(biāo)索引為0-57的數(shù)據(jù)才會被寫入到通道中。

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

FileChannel類型,可直接把數(shù)據(jù)傳輸?shù)?b>另一個channel。

transferFrom():數(shù)據(jù)從通道源傳輸?shù)?b>FileChannel

transferTo():FileChannelchannel

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • Java NIO(New IO)是從Java 1.4版本開始引入的一個新的IO API,可以替代標(biāo)準(zhǔn)的Java I...
    JackChen1024閱讀 7,591評論 1 143
  • Java NIO(New IO)是從Java 1.4版本開始引入的一個新的IO API,可以替代標(biāo)準(zhǔn)的Java I...
    zhisheng_blog閱讀 1,133評論 0 7
  • Java NIO(New IO)是從Java 1.4版本開始引入的一個新的IO API,可以替代標(biāo)準(zhǔn)的Java I...
    編碼前線閱讀 2,291評論 0 5
  • ------NIO簡介(1)-------- NIO組件 channel,buffer,selector,pip,...
    任嘉平生愿閱讀 564評論 1 0
  • 牽引 案例: 美國教材
    春夏AI閱讀 200評論 0 0