NIO:1、Soecket Socket通常也稱作"套接字",用于描述IP地址和端口,是一個通信鏈的句柄。一個Socket由一個IP地址和一個端口號唯一確定。Socket的基本工作過程包含以下四個步驟:1、創建Socket;2、打開連接到Socket的輸入輸出流;3、按照一定的協議對Socket進行讀寫操作;4、關閉Socket。ServerSocket用于服務器端,Socket是建立網絡連接時使用的。在連接成功時,應用程序兩端都會產生一個Socket實例,操作這個實例,完成所需的會話。
Server:ServerSocket serverSocket =new ServerSocket(2013);Socket socket = serverSocket.accept(); //接受到此Socket的連接,在socket連接之前,將阻塞// 由Socket對象得到輸入流,并構造相應的BufferedReader對象BufferedReader bufferedReader =new BufferedReader(new InputStreamReader(socket.getInputStream()));// 獲取從客戶端讀入的字符串String result = bufferedReader.readLine(); //在獲取客戶端輸入之前,又將阻塞//服務端發送給客戶端PrintWriter printWriter =new PrintWriter(socket.getOutputStream());printWriter.print("hello Client, I am Server!");printWriter.flush();// 關閉SocketprintWriter.close();bufferedReader.close();socket.close();
Client:// 創建SocketSocket socket =new Socket("127.0.0.1",2013);socket.setSoTimeout(60000); // 60s超時//發給服務端PrintWriter printWriter =new PrintWriter(socket.getOutputStream(),true);printWriter.println("Hi, im cliet.");printWriter.flush();//接收服務端消息,由Socket對象得到輸入流BufferedReader bufferedReader =new BufferedReader(new InputStreamReader(socket.getInputStream()));String result = bufferedReader.readLine(); //若一直未接收到,則阻塞System.out.println("Server say : " + result);
2、IO阻塞
當一個IO讀操作發生時,通常經歷兩個步驟:1,等待數據準備2,將數據從系統內核拷貝到操作進程中JDK1.4提供了對非阻塞IO(NIO)的支持,JDK1.5_update10版本使用epoll替代了傳統的select/poll,極大的提升了NIO通信的性能I/O 模型可以分為:同步阻塞,同步非阻塞,異步阻塞,異步非阻塞同步阻塞:在此種方式下,用戶進程在發起一個 IO 操作以后,必須等待 IO 操作的完成,只有當真正完成了 IO 操作以后,用戶進程才能運行。 JAVA傳統的 IO 模型屬于此種方式!同步非阻塞:用戶進程發起一個 IO 操作以后 邊可 返回做其它事情,但是用戶進程需要時不時的詢問 IO 操作是否就緒,這就要求用戶進程不停的去詢問,從而引入不必要的 CPU 資源浪費。非阻塞:體現在,這個線程可以去干別的,不需要一直在這等著同步:體現在消息通知機制,這個線程仍然要定時的讀取stream,判斷數據有沒有準備好,client采用循環的方式去讀取,可以看出CPU大部分被浪費了其中目前 JAVA 的 NIO 就屬于同步非阻塞 IO 。異步非阻塞:服務端調用read()方法,若stream中無數據則返回,程序繼續向下執行。 當stream中有數據時,操作系統會負責把數據拷貝到用戶空間,然后通知這個線程,這里的消息通知機制就是異步! 而不是像NIO那樣,自己起一個線程去監控stream里面有沒有數據!
IO多路復用 & Reactor模式(反應器模式,事件驅動):https://zhuanlan.zhihu.com/p/27382996https://zhuanlan.zhihu.com/p/27419141
3、NIO三大組件
Chanel/Buffer/Selector
通道和緩沖區,NIO最主要的兩個組件。NIO 是基于塊 (Block) 的,它以塊為基本單位處理數據Buffer 是一塊連續的內存塊,是 NIO 讀寫數據的中轉地。通道標識緩沖數據的源頭或者目的地,它用于向緩沖讀取或者寫入數據Chanel 是一個雙向通道,可讀亦可寫,對應于傳統IO的流Stream,Stream是單向的關系:數據可以從通道讀入緩沖區,也可以從緩沖區寫入到通道中。int bytesRead = inChannel.read(buf); //read into buffer.int bytesWritten = inChannel.write(buf);//read from buffer into channel.這里的read/write都是channel提供的,故都是以channel角度來看的:read,從channel讀數據 => 寫入到buf;write,寫數據到channel=> 從buf 讀數據Buffer:capacity:作為一個內存塊,Buffer有一個固定的大小值。一旦Buffer滿了,需要將其清空(通過讀數據或者清除數據)才能繼續寫數據往里寫數據limit:在讀模式下,Buffer的limit表示你最多能往Buffer里寫多少數據。寫模式下,limit等于Buffer的capacityposition:寫數據到Buffer中時,position表示當前的位置。初始的position值為0.position最大可為capacity – 1. 讀取數據時,也是從某個特定位置讀,當從Buffer的position處讀取數據時,position向前移動到下一個可讀的位置。 當將Buffer從寫模式切換到讀模式,position會被重置為0.?
Selector:http://www.molotang.com/articles/906.htmlhttps://segmentfault.com/a/1190000006824196http://ifeve.com/selectors/
1、通過 Selector.open() 打開一個 Selector.2、將 Channel 注冊到 Selector 中, 并設置需要監聽的事件(interest set)3、不斷重復: 調用 select() 方法, 調用 selector.selectedKeys() 獲取 selected keys,迭代每個 selected key: 從 selected key 中獲取 對應的 Channel ,判斷是哪些 IO 事件已經就緒了, 然后處理它們; 將已經處理過的 key 從 selected keys 集合中刪除.// 打開服務端 SocketServerSocketChannel serverSocketChannel = ServerSocketChannel.open();// 打開 SelectorSelector selector = Selector.open();// 服務端 Socket 監聽8080端口, 并配置為非阻塞模式serverSocketChannel.socket().bind(new InetSocketAddress(8080));serverSocketChannel.configureBlocking(false);// 將 channel 注冊到 selector 中.// 通常先注冊一個 OP_ACCEPT 事件, 然后在 OP_ACCEPT 到來時, 再將這個 Channel 的 OP_READ,注冊到 Selector 中。serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);//不斷輪詢while (true) { // 通過調用 select 方法, 阻塞地等待 channel I/O 可操作 if (selector.select(TIMEOUT) == 0) { System.out.print("."); continue; } // 獲取 I/O 操作就緒的 SelectionKey, 通過 SelectionKey 可以知道哪些 Channel 的哪類 I/O 操作已經就緒. Iterator keyIterator = selector.selectedKeys().iterator();
}