NIO-概覽
NIO-Buffer
NIO-Channel
NIO-Channel接口分析java
原本是想學習Netty的,可是Netty是一個NIO框架,所以在學習netty以前,仍是先梳理一下NIO的知識。經過剖析源碼理解NIO的設計原理。windows
本系列文章針對的是JDK1.8.0.161的源碼。微信
通道(Channel)是對原I/O包中的流的模擬。與文件設備I/O交互的全部數據都必須經過一個Channel對象。網絡
上一節咱們提到在NIO中使用緩衝區來存放指定基元的數據,咱們能夠經過Buffer來讀寫數據。
將數據寫入到硬盤時,咱們能夠將字節數據寫入到緩衝區中;若咱們要從硬盤讀取數據,則須要經過通道將數據寫入到緩衝區,而後再從緩衝區讀取數據。框架
根據不一樣的使用方式,分爲不一樣的通道。好比咱們須要網絡讀寫,就須要網絡交互的通道。須要文件讀寫就須要文件交互的通道。
NIO實現了Sctp協議、TCP協議、UDP協議以及文件傳輸四種通道,同時還實現了Windows平臺的異步Socket通道以及異步文件通道。dom
windows平臺的異步I/O是經過重疊I/O和IOCP(I/O完成端口)實現的,想要了解windows異步I/O的知識能夠看一下我另外一篇文章《Windows內核原理-同步IO與異步IO》異步
類型 | 通道 |
---|---|
Sctp協議客戶端 | SctpChannel |
Sctp協議多播客戶端 | SctpMultiChannel |
Sctp協議服務端 | SctpServerChannel |
UDP協議 | DatagramChannel |
TCP協議同步I/O服務端 | ServerSocketChannel |
TCP協議同步I/O客戶端 | ServerChannel |
文件讀寫 | FileChannel |
對於Windows平臺的異步通道socket
類型 | 通道 |
---|---|
TCP協議異步I/O服務端 | WindowsAsynchronousServerSocketChannel |
TCP協議異步I/O客戶端 | WindowsAsynchronousSocketChannel |
異步文件讀寫 | WindowsAsynchronousFileChannel |
另外NIO還實現了一個單向通信管道(Pipe)的功能,經過引入
SourceChannel
和SinkChannel
實現,底層實際仍是Socket通信。學習
在介紹不一樣的Channel的實現以前咱們先介紹下Channel如何使用。
以TCP協議爲例,咱們進行網絡收發的時候,首先須要建立一個ServerSocketChannel用於監聽端口。
//建立一個服務端socket通道用於接收鏈接 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); //綁定監聽地址 serverSocketChannel.socket().bind(new InetSocketAddress(8080)); //等待鏈接 SocketChannel socketChannel = serverSocketChannel.accept();
咱們監聽了8080端口。若沒有鏈接時,線程會阻塞在
accept
。
當有收到新的鏈接建立時,會獲取到SocketChannel,此時咱們須要建立一個Buffer用來從Channel中讀取數據。
ByteBuffer buf = ByteBuffer.allocate(1024); //數據將寫入到buffer中 int length = socketChannel.read(buf);
數據寫入到咱們的Buffer中,咱們就須要將他們讀出來
buf.flip(); //轉化爲可讀模式 byte[] data = new byte[length]; buf.get(data);
將數據從Buffer寫入到Channel時
buf.clear(); byte[] resp = {'O','K'}; buf.put(resp); buf.flip();//轉換爲讀模式 socketChannel.write(buf);
這裏爲了方便直接使用原來的Buffer。
做爲客戶端咱們須要建立一個SocketChannel。
SocketChannel.open(); client.connect(new InetSocketAddress("127.0.0.1", 6060));
發送HELLO
給服務端
ByteBuffer buffer = ByteBuffer.allocate(10); byte[] data = {'H', 'E', 'L', 'L', 'O'}; buffer.put(data); buffer.flip();//轉換爲讀模式 client.write(buffer);
阻塞等待讀取數據
buffer.clear(); client.read(buffer); buffer.flip();//轉換爲讀模式
處理完成,須要關閉釋放鏈接
//關閉客戶端輸入流 client.socket().shutdownInput(); //關閉客戶端輸出流 client.socket().shutdownOutput(); //關閉客戶端socket時會關閉客戶端channel client.socket().close(); //關閉客戶端channel,會同時關閉輸入和輸出流。 client.close();
關閉輸出流會發送FIN包,若輸入流未關閉仍然能夠繼續接收數據,這就是TCP的半鏈接。若處理完最後須要確保channel關閉。
FileChannel只能被FileInputStream、FileOutputStream、RandomAccessFile建立
使用RandomAccessFile
建立FileChannel
//第一個參數時文件名,第二個參數是讀寫方式 RandomAccessFile randomAccessFile = new RandomAccessFile("1.txt","rw"); FileChannel channel = randomAccessFile.getChannel();
使用RandomAccessFile
建立FileChannel
FileInputStream inputStream = new FileInputStream("1.txt"); channel = inputStream.getChannel();
inputStream獲取的FileChannel只能讀
使用RandomAccessFile
建立FileChannel
FileOutputStream outputStream = new FileOutputStream("1.txt"); channel = outputStream.getChannel();
inputStream獲取的FileChannel只能寫
關閉FileChannel的方法和關閉SocketChannel方法同樣。
//關閉channel時會關閉文件 channel.close(); //關閉文件時會關閉channel randomAccessFile.close(); //關閉文件流時會關閉channel inputStream.close(); //關閉文件流時會關閉channel inputStream.close();
因爲源碼解析的篇幅較長,所以將channel源碼單獨分出來說解。
微信掃一掃二維碼關注訂閱號傑哥技術分享
出處:http://www.javashuo.com/article/p-pwariqtr-gr.html 做者:傑哥很忙 本文使用「CC BY 4.0」創做共享協議。歡迎轉載,請在明顯位置給出出處及連接。