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