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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java NIO 介绍和基本demo

發布時間:2024/4/11 java 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java NIO 介绍和基本demo 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

IO操作主要可分為兩階段
1)把磁盤或者網絡數據加載到內核的內存空間
2)把內核的內存空間數據復制到用戶進程的內存空間中

阻塞、非阻塞的區別是在于第一階段,即數據準備階段。如果在數據準備時,主線程必須等待,就為阻塞;不需要一直等待可以執行其他操作,就是非阻塞。

同步、異步的區別在于第二階段,如果是用戶進程需要主動復制數據到用戶內存,則為同步;如果由內核完成數據報復制之后主動返回數據則為異步

前面說到,java中I/O編程,大致可以分為三種,阻塞IO(BIO)、非阻塞IO(NIO)和異步IO(AIO)。

BIO

BIO就是同步阻塞IO。在傳統的同步阻塞模型開發中,ServerSocket負責綁定IP地址,啟動監聽端口;Socket負責發起連接操作。連接成功后,雙方通過輸入和輸出流進行同步阻塞式通信。?傳統的socket通訊都是阻塞的,服務端接收到客戶端請求直到復制完數據都是阻塞的

AIO

異步非阻塞IO,式真正的異步IO,將數據報復制等操作交給內核完成,用戶進程可以處理其他事情而不需要干涉
底層過程同 NIO,區別在于,AIO 使用的命令是?epoll?,使用事件驅動的方式來代替輪詢的方式,當監聽的 I/O 準備好了,采用事件驅動(事件回調)的方式通知進程去獲取數據

NIO

同步非阻塞的IO,底層是采用操作系統的IO多路復用模型,通過操作系統的select()/epoll()方法監聽多個通道,一旦有一個channel數據報準備好,就通知應用程序去復制數據報。
非阻塞體現:一個 select 處理多個客戶應用進程的 I/O,如果第一個 I/O 數據沒有準備好,那么就去處理第二個客戶端的 I/O,依此類推,客戶端之間誰的數據先準備好就先處理誰的,不存在第二個要等第一個處理完才能開始處理的情況;

IO多路復用是阻塞在select,epoll這樣的系統調用之上,而沒有阻塞在真正的I/O系統調用之上。

提供了與傳統BIO模型中的Socket和ServerSocket相對應的SocketChannel和ServerSocketChannel兩種不同的套接字通道實現。兩種通道都支持阻塞和非阻塞兩種模式,默認采用阻塞的實現方式

1.緩沖區 Buffer

? ? 在 NIO 中,所有數據都是用緩沖區處理的。在讀取數據時,它是直接讀到緩沖區中的; 在寫入數據時,它也是寫入到緩沖區中的。所有的緩沖區類型都繼承于抽象類 Buffer,比方說:ByteBuffe、CharBuffer、 ShortBuffer、IntBuffer、LongBuffer、FloatBuffer、DoubleBuffer.都實現了相同的Buffer接口。
緩沖區本質上是一個數組,并提供了跟蹤和記錄緩沖區的狀態變化的信息。

? 其中最重要的屬性是下面三個,它們一起合作完成對緩沖區內部狀態的變化跟蹤:
? position:當前操作數據所在的位置,也可以理解做游標,當調用?get()/put()方法讀取或者寫入緩沖區的時候,position會自 ? ?動更新,在新創建一個 Buffer 對象時,position 初始值為0

? limit:指定還有多少數據需要取出(在從緩沖區寫入通道時),或者還有多少空間可以放入數據(在從通道讀入緩沖區時)。
??讀取緩沖區的數據時,如果limit > position 則認為在緩沖區還有數據可以讀取

? capacity:指定可以存儲在緩沖區中的最大數據容量,實際上,它指定了底層數組的大小
? 這三個屬性之間滿足:0 <=position <= limit <=capacity 的關系

使用緩沖區讀取和寫入數據通常遵循以下四個步驟:

  • ?寫數據到緩沖區;
  • 調用buffer.flip()方法;
  • 從緩沖區中讀取數據;
  • 調用buffer.clear()

? 在向buffer中寫入數據時,position會記錄下當前數據寫入的位置,如果寫入完成需要讀取數據,那么就需要通過flip()方法將Buffer從寫模式切換到讀模式,其實就是鎖定操作范圍,讓數據操作范圍索引只能在position - limit 之間,源碼如下。讀取完所有的數據后,就需要清空緩沖區,使得buffer可以再次被寫入

//完成兩件事: //1. 把limit 設置為當前的 position 值 //2. 把position 設置為0 public final Buffer flip() {limit = position;position = 0;mark = -1;return this;}

2.通道 Channel

????我們對數據的讀取和寫入要通過Channel,它就像水管一樣,是一個通道。通道區別于流的地方在于通道是雙向的,可以用于讀、寫或者同時讀寫操作。 NIO中,任何時候讀取數據,都不是直接從通道讀取,而是從通道讀取到緩沖區。然后再操作緩沖區中的數據

????操作系統底層的通道一般都是全雙工的,所以全雙工的Channel比Stream能更好的映射底層操作系統的API。

????Channel主要分兩大類:
????SelectableChannel:用戶網絡讀寫
????FileChannel:用于文件操作
????后面代碼會涉及的ServerSocketChannel和SocketChannel都是SelectableChannel的子類。

3.多路復用器 Selector

????Selector是Java ?NIO 編程的基礎。

????Selector--選擇器,顧名思義就是提供選擇已經就緒的任務的能力:Selector會不斷輪詢注冊在其上的Channel,查看是否某個Channel讀或者寫事件就緒。我們可以通過SelectionKey獲取就緒Channel的集合,然后根據其狀態進行后續的操作。

????一個Selector可以同時輪詢多個Channel,因為JDK使用了epoll()代替傳統的select實現,所以沒有最大連接句柄1024/2048的限制。所以,只需要一個線程負責Selector的輪詢,就可以接入成千上萬的客戶端。

服務端

/*** 2020/3/8* created by chenpp*/ public class NioServer {private int port;private static Selector selector = null;/*** 指定端口號啟動服務* */public boolean startServer(int port){try {this.port = port;selector = Selector.open();//打開監聽通道ServerSocketChannel server = ServerSocketChannel.open();//綁定端口server.bind(new InetSocketAddress(this.port));//默認configureBlocking為true,如果為 true,此通道將被置于阻塞模式;如果為 false.則此通道將被置于非阻塞模式server.configureBlocking(false);//創建選擇器selector = Selector.open();//監聽客戶端連接請求server.register(selector, SelectionKey.OP_ACCEPT);System.out.println("服務端啟動成功,監聽端口:" + port);}catch (Exception e){System.out.println("服務器啟動失敗");return false;}return true;}public void listen() throws IOException {while(true){//阻塞方法,輪詢注冊的channel,當至少一個channel就緒的時候才會繼續往下執行int keyCount = selector.select();System.out.println("當前有:"+keyCount+"channel有事件就緒");//獲取就緒的SelectionKeySet<SelectionKey> keys = selector.selectedKeys();Iterator<SelectionKey> it = keys.iterator();SelectionKey key = null;//迭代就緒的keywhile(it.hasNext()){key = it.next();it.remove();//SelectionKey相當于是一個Channel的表示,標記當前channel處于什么狀態// 按照channel的不同狀態處理數據process(key);}}}private void process(SelectionKey key) throws IOException {//該channel已就緒,可接收消息if(key.isAcceptable()){System.out.println("accept事件就緒...");doAccept(key);}else if(key.isReadable()){System.out.println("read事件就緒...");doRead(key);}else if(key.isWritable()){System.out.println("write事件就緒...");doWrite(key);}}private void doWrite(SelectionKey key) throws IOException {//獲取對應的socketSocketChannel socket = (SocketChannel)key.channel();//獲取key上的附件String content = (String)key.attachment();socket.write(ByteBuffer.wrap(content.getBytes()));socket.close();}private void doRead(SelectionKey key) throws IOException {//獲取對應的socketSocketChannel socket = (SocketChannel)key.channel();//設置一個讀取數據的Buffer 大小為1024ByteBuffer buff = ByteBuffer.allocate(1024);StringBuilder content = new StringBuilder();while(socket.read(buff) > 0) {buff.flip();content.append(new String(buff.array(),"utf-8"));}//注冊selector,并設置為可寫模式key = socket.register(selector,SelectionKey.OP_WRITE);//在key上攜帶一個附件(附近就是之后要寫的內容)key.attach("服務端已收到:"+content);System.out.println("讀取內容:" + content);}private void doAccept(SelectionKey key) throws IOException {//獲取對應的channelServerSocketChannel server = (ServerSocketChannel)key.channel();//從channel中獲取socket信息SocketChannel socket = server.accept();//設置為非阻塞模式socket.configureBlocking(false);//注冊selector,并設置為可讀模式socket.register(selector, SelectionKey.OP_READ);}} /*** 2020/3/8* created by chenpp*/ public class NioServerStarter {public static void main(String[] args) throws IOException {NioServer nioServer = new NioServer();nioServer.startServer(8080);nioServer.listen();} }

客戶端

package com.chenpp.nio;import javax.sound.midi.SoundbankResource; 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.Set; import java.util.UUID;/*** 2020/3/8* created by chenpp*/ public class NioClient {private static Selector selector = null;public void start(String ip, int port) throws IOException {//創建選擇器selector = Selector.open();//打開監聽通道SocketChannel socketChannel = SocketChannel.open();socketChannel.configureBlocking(false);//連接對應的服務器 ip , portsocketChannel.connect(new InetSocketAddress(ip, port));//注冊select為連接狀態socketChannel.register(selector, SelectionKey.OP_CONNECT);System.out.println("客戶端,啟動成功...");}public void listen() throws IOException {while (true) {//阻塞方法,輪詢注冊的channel,當至少一個channel就緒的時候才會繼續往下執行selector.select();//獲取就緒的SelectionKeySet<SelectionKey> keys = selector.selectedKeys();Iterator<SelectionKey> it = keys.iterator();SelectionKey key = null;//迭代就緒的keywhile (it.hasNext()) {key = it.next();it.remove();//SelectionKey相當于是一個Channel的表示,標記當前channel處于什么狀態// 按照channel的不同狀態處理數據process(key);}}}private void process(SelectionKey key) throws IOException {//channel處于可連接狀態,發送消息給服務端if (key.isConnectable()) {System.out.println("connect事件就緒 ....");SocketChannel clientChannel = (SocketChannel) key.channel();if (clientChannel.isConnectionPending()) {clientChannel.finishConnect();}clientChannel.configureBlocking(false);String name = UUID.randomUUID().toString();System.out.println("客戶端發送數據:{}" + name);ByteBuffer buffer = ByteBuffer.wrap(name.getBytes());clientChannel.write(buffer);clientChannel.register(key.selector(), SelectionKey.OP_READ);} else if (key.isReadable()) {//獲取對應的socketSystem.out.println("read事件就緒 ....");SocketChannel socket = (SocketChannel) key.channel();//設置一個讀取數據的Buffer 大小為1024ByteBuffer buff = ByteBuffer.allocate(1024);StringBuilder content = new StringBuilder();int len = socket.read(buff);if (len > 0) {buff.flip();content.append(new String(buff.array(), "utf-8"));//讓客戶端讀取下一次readSystem.out.println("客戶端收到反饋:" + content);key.interestOps(SelectionKey.OP_READ);}else if(len <= 0){key.cancel();socket.close();}}}} /*** 2020/3/8* created by chenpp*/ public class NioClientStarter {public static void main(String[] args) throws IOException {NioClient client = new NioClient();client.start("localhost",8080);client.listen();} }

NIO工作原理:

  • 由一個專門的線程來處理所有的 IO 事件,并負責分發。
  • 事件驅動機制:事件到達的時候觸發,而不是同步的去監視事件。


參考:

https://blog.csdn.net/anxpp/article/details/51512200

總結

以上是生活随笔為你收集整理的Java NIO 介绍和基本demo的全部內容,希望文章能夠幫你解決所遇到的問題。

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