摘要:后改良為用線程池的方式代替新增線程,被稱(chēng)為偽異步。最大的問(wèn)題是阻塞,同步。每次請(qǐng)求都由程序執(zhí)行并返回,這是同步的缺陷。這些都會(huì)被注冊(cè)在多路復(fù)用器上。多路復(fù)用器提供選擇已經(jīng)就緒狀態(tài)任務(wù)的能力。并沒(méi)有采用的多路復(fù)用器,而是使用異步通道的概念。
Netty是一個(gè)提供異步事件驅(qū)動(dòng)的網(wǎng)絡(luò)應(yīng)用框架,用以快速開(kāi)發(fā)高性能、高可靠的網(wǎng)絡(luò)服務(wù)器和客戶(hù)端程序。Netty簡(jiǎn)化了網(wǎng)絡(luò)程序的開(kāi)發(fā),是很多框架和公司都在使用的技術(shù)。更是面試的加分項(xiàng)。Netty并非橫空出世,它是在BIO,NIO,AIO演變中的產(chǎn)物,是一種NIO框架。而B(niǎo)IO,NIO,AIO更是筆試中要考,面試中要問(wèn)的技術(shù)。也是一個(gè)很好的加分項(xiàng),加分就是加工資,你還在等什么?本章帶你細(xì)細(xì)品味三者的不同!
流程圖:
BIO NIO AIO 流程圖
技術(shù):BIO,NIO,AIO
說(shuō)明:github上有更全的源碼。
源碼:https://github.com/ITDragonBl...
BIO 全稱(chēng)Block-IO 是一種阻塞同步的通信模式。我們常說(shuō)的Stock IO 一般指的是BIO。是一個(gè)比較傳統(tǒng)的通信方式,模式簡(jiǎn)單,使用方便。但并發(fā)處理能力低,通信耗時(shí),依賴(lài)網(wǎng)速。
BIO 設(shè)計(jì)原理:服務(wù)器通過(guò)一個(gè)Acceptor線程負(fù)責(zé)監(jiān)聽(tīng)客戶(hù)端請(qǐng)求和為每個(gè)客戶(hù)端創(chuàng)建一個(gè)新的線程進(jìn)行鏈路處理。典型的一請(qǐng)求一應(yīng)答模式。若客戶(hù)端數(shù)量增多,頻繁地創(chuàng)建和銷(xiāo)毀線程會(huì)給服務(wù)器打開(kāi)很大的壓力。后改良為用線程池的方式代替新增線程,被稱(chēng)為偽異步IO。
服務(wù)器提供IP地址和監(jiān)聽(tīng)的端口,客戶(hù)端通過(guò)TCP的三次握手與服務(wù)器連接,連接成功后,雙放才能通過(guò)套接字(Stock)通信。
小結(jié):BIO模型中通過(guò)Socket和ServerSocket完成套接字通道的實(shí)現(xiàn)。阻塞,同步,建立連接耗時(shí)。
BIO服務(wù)器代碼,負(fù)責(zé)啟動(dòng)服務(wù),阻塞服務(wù),監(jiān)聽(tīng)客戶(hù)端請(qǐng)求,新建線程處理任務(wù)。
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
IO 也稱(chēng)為 BIO,Block IO 阻塞同步的通訊方式
比較傳統(tǒng)的技術(shù),實(shí)際開(kāi)發(fā)中基本上用Netty或者是AIO。熟悉BIO,NIO,體會(huì)其中變化的過(guò)程。作為一個(gè)web開(kāi)發(fā)人員,stock通訊面試經(jīng)常問(wèn)題。
BIO最大的問(wèn)題是:阻塞,同步。
BIO通訊方式很依賴(lài)于網(wǎng)絡(luò),若網(wǎng)速不好,阻塞時(shí)間會(huì)很長(zhǎng)。每次請(qǐng)求都由程序執(zhí)行并返回,這是同步的缺陷。
BIO工作流程:
第一步:server端服務(wù)器啟動(dòng)
第二步:server端服務(wù)器阻塞監(jiān)聽(tīng)client請(qǐng)求
第三步:server端服務(wù)器接收請(qǐng)求,創(chuàng)建線程實(shí)現(xiàn)任務(wù)
*/
public class ITDragonBIOServer {
private static final Integer PORT = 8888; // 服務(wù)器對(duì)外的端口號(hào) public static void main(String[] args) { ServerSocket server = null; Socket socket = null; ThreadPoolExecutor executor = null; try { server = new ServerSocket(PORT); // ServerSocket 啟動(dòng)監(jiān)聽(tīng)端口 System.out.println("BIO Server 服務(wù)器啟動(dòng)........."); /*--------------傳統(tǒng)的新增線程處理----------------*/ /*while (true) { // 服務(wù)器監(jiān)聽(tīng):阻塞,等待Client請(qǐng)求 socket = server.accept(); System.out.println("server 服務(wù)器確認(rèn)請(qǐng)求 : " + socket); // 服務(wù)器連接確認(rèn):確認(rèn)Client請(qǐng)求后,創(chuàng)建線程執(zhí)行任務(wù) 。很明顯的問(wèn)題,若每接收一次請(qǐng)求就要?jiǎng)?chuàng)建一個(gè)線程,顯然是不合理的。 new Thread(new ITDragonBIOServerHandler(socket)).start(); } */ /*--------------通過(guò)線程池處理緩解高并發(fā)給程序帶來(lái)的壓力(偽異步IO編程)----------------*/ executor = new ThreadPoolExecutor(10, 100, 1000, TimeUnit.SECONDS, new ArrayBlockingQueue(50)); while (true) { socket = server.accept(); // 服務(wù)器監(jiān)聽(tīng):阻塞,等待Client請(qǐng)求 ITDragonBIOServerHandler serverHandler = new ITDragonBIOServerHandler(socket); executor.execute(serverHandler); } } catch (IOException e) { e.printStackTrace(); } finally { try { if (null != socket) { socket.close(); socket = null; } if (null != server) { server.close(); server = null; System.out.println("BIO Server 服務(wù)器關(guān)閉了!!!!"); } executor.shutdown(); } catch (IOException e) { e.printStackTrace(); } } }
}
BIO服務(wù)端處理任務(wù)代碼,負(fù)責(zé)處理Stock套接字,返回套接字給客戶(hù)端,解耦。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import com.itdragon.util.CalculatorUtil;
public class ITDragonBIOServerHandler implements Runnable{
private Socket socket;
public ITDragonBIOServerHandler(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
BufferedReader reader = null; PrintWriter writer = null; try { reader = new BufferedReader(new InputStreamReader(this.socket.getInputStream())); writer = new PrintWriter(this.socket.getOutputStream(), true); String body = null; while (true) { body = reader.readLine(); // 若客戶(hù)端用的是 writer.print() 傳值,那readerLine() 是不能獲取值,細(xì)節(jié) if (null == body) { break; } System.out.println("server服務(wù)端接收參數(shù) : " + body); writer.println(body + " = " + CalculatorUtil.cal(body).toString()); } } catch (IOException e) { e.printStackTrace(); } finally { if (null != writer) { writer.close(); } try { if (null != reader) { reader.close(); } if (null != this.socket) { this.socket.close(); this.socket = null; } } catch (IOException e) { e.printStackTrace(); } }
}
}
BIO客戶(hù)端代碼,負(fù)責(zé)啟動(dòng)客戶(hù)端,向服務(wù)器發(fā)送請(qǐng)求,接收服務(wù)器返回的Stock套接字。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Random;
/**
BIO 客戶(hù)端
Socket : 向服務(wù)端發(fā)送連接
PrintWriter : 向服務(wù)端傳遞參數(shù)
BufferedReader : 從服務(wù)端接收參數(shù)
*/
public class ITDragonBIOClient {
private static Integer PORT = 8888; private static String IP_ADDRESS = "127.0.0.1"; public static void main(String[] args) { for (int i = 0; i < 10; i++) { clientReq(i); } } private static void clientReq(int i) { Socket socket = null; BufferedReader reader = null; PrintWriter writer = null; try { socket = new Socket(IP_ADDRESS, PORT); // Socket 發(fā)起連接操作。連接成功后,雙方通過(guò)輸入和輸出流進(jìn)行同步阻塞式通信 reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); // 獲取返回內(nèi)容 writer = new PrintWriter(socket.getOutputStream(), true); String []operators = {"+","-","*","/"}; Random random = new Random(System.currentTimeMillis()); String expression = random.nextInt(10)+operators[random.nextInt(4)]+(random.nextInt(10)+1); writer.println(expression); // 向服務(wù)器端發(fā)送數(shù)據(jù) System.out.println(i + " 客戶(hù)端打印返回?cái)?shù)據(jù) : " + reader.readLine()); } catch (Exception e) { e.printStackTrace(); } finally { try { if (null != reader) { reader.close(); } if (null != socket) { socket.close(); socket = null; } } catch (IOException e) { e.printStackTrace(); } } }
}
NIO
NIO 全稱(chēng)New IO,也叫Non-Block IO 是一種非阻塞同步的通信模式。
NIO 設(shè)計(jì)原理:
NIO 相對(duì)于BIO來(lái)說(shuō)一大進(jìn)步。客戶(hù)端和服務(wù)器之間通過(guò)Channel通信。NIO可以在Channel進(jìn)行讀寫(xiě)操作。這些Channel都會(huì)被注冊(cè)在Selector多路復(fù)用器上。Selector通過(guò)一個(gè)線程不停的輪詢(xún)這些Channel。找出已經(jīng)準(zhǔn)備就緒的Channel執(zhí)行IO操作。
NIO 通過(guò)一個(gè)線程輪詢(xún),實(shí)現(xiàn)千萬(wàn)個(gè)客戶(hù)端的請(qǐng)求,這就是非阻塞NIO的特點(diǎn)。
1)緩沖區(qū)Buffer:它是NIO與BIO的一個(gè)重要區(qū)別。BIO是將數(shù)據(jù)直接寫(xiě)入或讀取到Stream對(duì)象中。而NIO的數(shù)據(jù)操作都是在緩沖區(qū)中進(jìn)行的。緩沖區(qū)實(shí)際上是一個(gè)數(shù)組。Buffer最常見(jiàn)的類(lèi)型是ByteBuffer,另外還有CharBuffer,ShortBuffer,IntBuffer,LongBuffer,F(xiàn)loatBuffer,DoubleBuffer。
2)通道Channel:和流不同,通道是雙向的。NIO可以通過(guò)Channel進(jìn)行數(shù)據(jù)的讀,寫(xiě)和同時(shí)讀寫(xiě)操作。通道分為兩大類(lèi):一類(lèi)是網(wǎng)絡(luò)讀寫(xiě)(SelectableChannel),一類(lèi)是用于文件操作(FileChannel),我們使用的SocketChannel和ServerSocketChannel都是SelectableChannel的子類(lèi)。
3)多路復(fù)用器Selector:NIO編程的基礎(chǔ)。多路復(fù)用器提供選擇已經(jīng)就緒的任務(wù)的能力。就是Selector會(huì)不斷地輪詢(xún)注冊(cè)在其上的通道(Channel),如果某個(gè)通道處于就緒狀態(tài),會(huì)被Selector輪詢(xún)出來(lái),然后通過(guò)SelectionKey可以取得就緒的Channel集合,從而進(jìn)行后續(xù)的IO操作。服務(wù)器端只要提供一個(gè)線程負(fù)責(zé)Selector的輪詢(xún),就可以接入成千上萬(wàn)個(gè)客戶(hù)端,這就是JDK NIO庫(kù)的巨大進(jìn)步。
說(shuō)明:這里的代碼只實(shí)現(xiàn)了客戶(hù)端發(fā)送請(qǐng)求,服務(wù)端接收數(shù)據(jù)的功能。其目的是簡(jiǎn)化代碼,方便理解。github源碼中有完整代碼。
小結(jié):NIO模型中通過(guò)SocketChannel和ServerSocketChannel完成套接字通道的實(shí)現(xiàn)。非阻塞/阻塞,同步,避免TCP建立連接使用三次握手帶來(lái)的開(kāi)銷(xiāo)。
NIO服務(wù)器代碼,負(fù)責(zé)開(kāi)啟多路復(fù)用器,打開(kāi)通道,注冊(cè)通道,輪詢(xún)通道,處理通道。
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.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
/**
NIO 也稱(chēng) New IO, Non-Block IO,非阻塞同步通信方式
從BIO的阻塞到NIO的非阻塞,這是一大進(jìn)步。功歸于Buffer,Channel,Selector三個(gè)設(shè)計(jì)實(shí)現(xiàn)。
Buffer : 緩沖區(qū)。NIO的數(shù)據(jù)操作都是在緩沖區(qū)中進(jìn)行。緩沖區(qū)實(shí)際上是一個(gè)數(shù)組。而B(niǎo)IO是將數(shù)據(jù)直接寫(xiě)入或讀取到Stream對(duì)象。
Channel : 通道。NIO可以通過(guò)Channel進(jìn)行數(shù)據(jù)的讀,寫(xiě)和同時(shí)讀寫(xiě)操作。
Selector : 多路復(fù)用器。NIO編程的基礎(chǔ)。多路復(fù)用器提供選擇已經(jīng)就緒狀態(tài)任務(wù)的能力。
客戶(hù)端和服務(wù)器通過(guò)Channel連接,而這些Channel都要注冊(cè)在Selector。Selector通過(guò)一個(gè)線程不停的輪詢(xún)這些Channel。找出已經(jīng)準(zhǔn)備就緒的Channel執(zhí)行IO操作。
NIO通過(guò)一個(gè)線程輪詢(xún),實(shí)現(xiàn)千萬(wàn)個(gè)客戶(hù)端的請(qǐng)求,這就是非阻塞NIO的特點(diǎn)。
*/
public class ITDragonNIOServer implements Runnable{
private final int BUFFER_SIZE = 1024; // 緩沖區(qū)大小
private final int PORT = 8888; // 監(jiān)聽(tīng)的端口
private Selector selector; // 多路復(fù)用器,NIO編程的基礎(chǔ),負(fù)責(zé)管理通道Channel
private ByteBuffer readBuffer = ByteBuffer.allocate(BUFFER_SIZE); // 緩沖區(qū)Buffer
public ITDragonNIOServer() {
startServer();
}
private void startServer() {
try { // 1.開(kāi)啟多路復(fù)用器 selector = Selector.open(); // 2.打開(kāi)服務(wù)器通道(網(wǎng)絡(luò)讀寫(xiě)通道) ServerSocketChannel channel = ServerSocketChannel.open(); // 3.設(shè)置服務(wù)器通道為非阻塞模式,true為阻塞,false為非阻塞 channel.configureBlocking(false); // 4.綁定端口 channel.socket().bind(new InetSocketAddress(PORT)); // 5.把通道注冊(cè)到多路復(fù)用器上,并監(jiān)聽(tīng)阻塞事件 /** * SelectionKey.OP_READ : 表示關(guān)注讀數(shù)據(jù)就緒事件 * SelectionKey.OP_WRITE : 表示關(guān)注寫(xiě)數(shù)據(jù)就緒事件 * SelectionKey.OP_CONNECT: 表示關(guān)注socket channel的連接完成事件 * SelectionKey.OP_ACCEPT : 表示關(guān)注server-socket channel的accept事件 */ channel.register(selector, SelectionKey.OP_ACCEPT); System.out.println("Server start >>>>>>>>> port :" + PORT); } catch (IOException e) { e.printStackTrace(); }
}
// 需要一個(gè)線程負(fù)責(zé)Selector的輪詢(xún)
@Override
public void run() {
while (true) { try { /** * a.select() 阻塞到至少有一個(gè)通道在你注冊(cè)的事件上就緒 * b.select(long timeOut) 阻塞到至少有一個(gè)通道在你注冊(cè)的事件上就緒或者超時(shí)timeOut * c.selectNow() 立即返回。如果沒(méi)有就緒的通道則返回0 * select方法的返回值表示就緒通道的個(gè)數(shù)。 */ // 1.多路復(fù)用器監(jiān)聽(tīng)阻塞 selector.select(); // 2.多路復(fù)用器已經(jīng)選擇的結(jié)果集 IteratorselectionKeys = selector.selectedKeys().iterator(); // 3.不停的輪詢(xún) while (selectionKeys.hasNext()) { // 4.獲取一個(gè)選中的key SelectionKey key = selectionKeys.next(); // 5.獲取后便將其從容器中移除 selectionKeys.remove(); // 6.只獲取有效的key if (!key.isValid()){ continue; } // 阻塞狀態(tài)處理 if (key.isAcceptable()){ accept(key); } // 可讀狀態(tài)處理 if (key.isReadable()){ read(key); } } } catch (IOException e) { e.printStackTrace(); } }
}
// 設(shè)置阻塞,等待Client請(qǐng)求。在傳統(tǒng)IO編程中,用的是ServerSocket和Socket。在NIO中采用的ServerSocketChannel和SocketChannel
private void accept(SelectionKey selectionKey) {
try { // 1.獲取通道服務(wù) ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectionKey.channel(); // 2.執(zhí)行阻塞方法 SocketChannel socketChannel = serverSocketChannel.accept(); // 3.設(shè)置服務(wù)器通道為非阻塞模式,true為阻塞,false為非阻塞 socketChannel.configureBlocking(false); // 4.把通道注冊(cè)到多路復(fù)用器上,并設(shè)置讀取標(biāo)識(shí) socketChannel.register(selector, SelectionKey.OP_READ); } catch (IOException e) { e.printStackTrace(); }
}
private void read(SelectionKey selectionKey) {
try { // 1.清空緩沖區(qū)數(shù)據(jù) readBuffer.clear(); // 2.獲取在多路復(fù)用器上注冊(cè)的通道 SocketChannel socketChannel = (SocketChannel) selectionKey.channel(); // 3.讀取數(shù)據(jù),返回 int count = socketChannel.read(readBuffer); // 4.返回內(nèi)容為-1 表示沒(méi)有數(shù)據(jù) if (-1 == count) { selectionKey.channel().close(); selectionKey.cancel(); return ; } // 5.有數(shù)據(jù)則在讀取數(shù)據(jù)前進(jìn)行復(fù)位操作 readBuffer.flip(); // 6.根據(jù)緩沖區(qū)大小創(chuàng)建一個(gè)相應(yīng)大小的bytes數(shù)組,用來(lái)獲取值 byte[] bytes = new byte[readBuffer.remaining()]; // 7.接收緩沖區(qū)數(shù)據(jù) readBuffer.get(bytes); // 8.打印獲取到的數(shù)據(jù) System.out.println("NIO Server : " + new String(bytes)); // 不能用bytes.toString() } catch (IOException e) { e.printStackTrace(); }
}
public static void main(String[] args) {
new Thread(new ITDragonNIOServer()).start();
}
}
NIO客戶(hù)端代碼,負(fù)責(zé)連接服務(wù)器,聲明通道,連接通道
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
public class ITDragonNIOClient {
private final static int PORT = 8888; private final static int BUFFER_SIZE = 1024; private final static String IP_ADDRESS = "127.0.0.1"; public static void main(String[] args) { clientReq(); } private static void clientReq() { // 1.創(chuàng)建連接地址 InetSocketAddress inetSocketAddress = new InetSocketAddress(IP_ADDRESS, PORT); // 2.聲明一個(gè)連接通道 SocketChannel socketChannel = null; // 3.創(chuàng)建一個(gè)緩沖區(qū) ByteBuffer byteBuffer = ByteBuffer.allocate(BUFFER_SIZE); try { // 4.打開(kāi)通道 socketChannel = SocketChannel.open(); // 5.連接服務(wù)器 socketChannel.connect(inetSocketAddress); while(true){ // 6.定義一個(gè)字節(jié)數(shù)組,然后使用系統(tǒng)錄入功能: byte[] bytes = new byte[BUFFER_SIZE]; // 7.鍵盤(pán)輸入數(shù)據(jù) System.in.read(bytes); // 8.把數(shù)據(jù)放到緩沖區(qū)中 byteBuffer.put(bytes); // 9.對(duì)緩沖區(qū)進(jìn)行復(fù)位 byteBuffer.flip(); // 10.寫(xiě)出數(shù)據(jù) socketChannel.write(byteBuffer); // 11.清空緩沖區(qū)數(shù)據(jù) byteBuffer.clear(); } } catch (IOException e) { e.printStackTrace(); } finally { if (null != socketChannel) { try { socketChannel.close(); } catch (IOException e) { e.printStackTrace(); } } } }
}
AIO
AIO 也叫NIO2.0 是一種非阻塞異步的通信模式。在NIO的基礎(chǔ)上引入了新的異步通道的概念,并提供了異步文件通道和異步套接字通道的實(shí)現(xiàn)。
AIO 并沒(méi)有采用NIO的多路復(fù)用器,而是使用異步通道的概念。其read,write方法的返回類(lèi)型都是Future對(duì)象。而Future模型是異步的,其核心思想是:去主函數(shù)等待時(shí)間。
小結(jié):AIO模型中通過(guò)AsynchronousSocketChannel和AsynchronousServerSocketChannel完成套接字通道的實(shí)現(xiàn)。非阻塞,異步。
AIO服務(wù)端代碼,負(fù)責(zé)創(chuàng)建服務(wù)器通道,綁定端口,等待請(qǐng)求。
import java.net.InetSocketAddress;
import java.nio.channels.AsynchronousChannelGroup;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
AIO, 也叫 NIO2.0 是一種異步非阻塞的通信方式
AIO 引入了異步通道的概念 AsynchronousServerSocketChannel和AsynchronousSocketChannel 其read和write方法返回值類(lèi)型是Future對(duì)象。
*/
public class ITDragonAIOServer {
private ExecutorService executorService; // 線程池 private AsynchronousChannelGroup threadGroup; // 通道組 public AsynchronousServerSocketChannel asynServerSocketChannel; // 服務(wù)器通道 public void start(Integer port){ try { // 1.創(chuàng)建一個(gè)緩存池 executorService = Executors.newCachedThreadPool(); // 2.創(chuàng)建通道組 threadGroup = AsynchronousChannelGroup.withCachedThreadPool(executorService, 1); // 3.創(chuàng)建服務(wù)器通道 asynServerSocketChannel = AsynchronousServerSocketChannel.open(threadGroup); // 4.進(jìn)行綁定 asynServerSocketChannel.bind(new InetSocketAddress(port)); System.out.println("server start , port : " + port); // 5.等待客戶(hù)端請(qǐng)求 asynServerSocketChannel.accept(this, new ITDragonAIOServerHandler()); // 一直阻塞 不讓服務(wù)器停止,真實(shí)環(huán)境是在tomcat下運(yùn)行,所以不需要這行代碼 Thread.sleep(Integer.MAX_VALUE); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { ITDragonAIOServer server = new ITDragonAIOServer(); server.start(8888); }
}
AIO服務(wù)器任務(wù)處理代碼,負(fù)責(zé),讀取數(shù)據(jù),寫(xiě)入數(shù)據(jù)
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.ExecutionException;
import com.itdragon.util.CalculatorUtil;
public class ITDragonAIOServerHandler implements CompletionHandler
private final Integer BUFFER_SIZE = 1024;
@Override
public void completed(AsynchronousSocketChannel asynSocketChannel, ITDragonAIOServer attachment) {
// 保證多個(gè)客戶(hù)端都可以阻塞 attachment.asynServerSocketChannel.accept(attachment, this); read(asynSocketChannel);
}
//讀取數(shù)據(jù)
private void read(final AsynchronousSocketChannel asynSocketChannel) {
ByteBuffer byteBuffer = ByteBuffer.allocate(BUFFER_SIZE); asynSocketChannel.read(byteBuffer, byteBuffer, new CompletionHandler() { @Override public void completed(Integer resultSize, ByteBuffer attachment) { //進(jìn)行讀取之后,重置標(biāo)識(shí)位 attachment.flip(); //獲取讀取的數(shù)據(jù) String resultData = new String(attachment.array()).trim(); System.out.println("Server -> " + "收到客戶(hù)端的數(shù)據(jù)信息為:" + resultData); String response = resultData + " = " + CalculatorUtil.cal(resultData); write(asynSocketChannel, response); } @Override public void failed(Throwable exc, ByteBuffer attachment) { exc.printStackTrace(); } });
}
// 寫(xiě)入數(shù)據(jù)
private void write(AsynchronousSocketChannel asynSocketChannel, String response) {
try { // 把數(shù)據(jù)寫(xiě)入到緩沖區(qū)中 ByteBuffer buf = ByteBuffer.allocate(BUFFER_SIZE); buf.put(response.getBytes()); buf.flip(); // 在從緩沖區(qū)寫(xiě)入到通道中 asynSocketChannel.write(buf).get(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); }
}
@Override
public void failed(Throwable exc, ITDragonAIOServer attachment) {
exc.printStackTrace();
}
}
AIO客戶(hù)端代碼,負(fù)責(zé)連接服務(wù)器,聲明通道,連接通道
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.util.Random;
public class ITDragonAIOClient implements Runnable{
private static Integer PORT = 8888; private static String IP_ADDRESS = "127.0.0.1"; private AsynchronousSocketChannel asynSocketChannel ; public ITDragonAIOClient() throws Exception { asynSocketChannel = AsynchronousSocketChannel.open(); // 打開(kāi)通道 } public void connect(){ asynSocketChannel.connect(new InetSocketAddress(IP_ADDRESS, PORT)); // 創(chuàng)建連接 和NIO一樣 } public void write(String request){ try { asynSocketChannel.write(ByteBuffer.wrap(request.getBytes())).get(); ByteBuffer byteBuffer = ByteBuffer.allocate(1024); asynSocketChannel.read(byteBuffer).get(); byteBuffer.flip(); byte[] respByte = new byte[byteBuffer.remaining()]; byteBuffer.get(respByte); // 將緩沖區(qū)的數(shù)據(jù)放入到 byte數(shù)組中 System.out.println(new String(respByte,"utf-8").trim()); } catch (Exception e) { e.printStackTrace(); } } @Override public void run() { while(true){ } } public static void main(String[] args) throws Exception { for (int i = 0; i < 10; i++) { ITDragonAIOClient myClient = new ITDragonAIOClient(); myClient.connect(); new Thread(myClient, "myClient").start(); String []operators = {"+","-","*","/"}; Random random = new Random(System.currentTimeMillis()); String expression = random.nextInt(10)+operators[random.nextInt(4)]+(random.nextInt(10)+1); myClient.write(expression); } }
}
常見(jiàn)面試題
1 IO,NIO,AIO區(qū)別
IO 阻塞同步通信模式,客戶(hù)端和服務(wù)器連接需要三次握手,使用簡(jiǎn)單,但吞吐量小
NIO 非阻塞同步通信模式,客戶(hù)端與服務(wù)器通過(guò)Channel連接,采用多路復(fù)用器輪詢(xún)注冊(cè)的Channel。提高吞吐量和可靠性。
AIO 非阻塞異步通信模式,NIO的升級(jí)版,采用異步通道實(shí)現(xiàn)異步通信,其read和write方法均是異步方法。
2 Stock通信的偽代碼實(shí)現(xiàn)流程
服務(wù)器綁定端口:server = new ServerSocket(PORT)
服務(wù)器阻塞監(jiān)聽(tīng):socket = server.accept()
服務(wù)器開(kāi)啟線程:new Thread(Handle handle)
服務(wù)器讀寫(xiě)數(shù)據(jù):BufferedReader PrintWriter
客戶(hù)端綁定IP和PORT:new Socket(IP_ADDRESS, PORT)
客戶(hù)端傳輸接收數(shù)據(jù):BufferedReader PrintWriter
3 TCP協(xié)議與UDP協(xié)議有什么區(qū)別
TCP : 傳輸控制協(xié)議是基于連接的協(xié)議,在正式收發(fā)數(shù)據(jù)前,必須和對(duì)方建立可靠的連接。速度慢,合適傳輸大量數(shù)據(jù)。
UDP : 用戶(hù)數(shù)據(jù)報(bào)協(xié)議是與TCP相對(duì)應(yīng)的協(xié)議。面向非連接的協(xié)議,不與對(duì)方建立連接,而是直接就把數(shù)據(jù)包發(fā)送過(guò)去,速度快,適合傳輸少量數(shù)據(jù)。
4 什么是同步阻塞BIO,同步非阻塞NIO,異步非阻塞AIO
同步阻塞IO : 用戶(hù)進(jìn)程發(fā)起一個(gè)IO操作以后,必須等待IO操作的真正完成后,才能繼續(xù)運(yùn)行。
同步非阻塞IO: 用戶(hù)進(jìn)程發(fā)起一個(gè)IO操作以后,可做其它事情,但用戶(hù)進(jìn)程需要經(jīng)常詢(xún)問(wèn)IO操作是否完成,這樣造成不必要的CPU資源浪費(fèi)。
異步非阻塞IO: 用戶(hù)進(jìn)程發(fā)起一個(gè)IO操作然后,立即返回,等IO操作真正的完成以后,應(yīng)用程序會(huì)得到IO操作完成的通知。類(lèi)比Future模式。
總結(jié)
1 BIO模型中通過(guò)Socket和ServerSocket完成套接字通道實(shí)現(xiàn)。阻塞,同步,連接耗時(shí)。
2 NIO模型中通過(guò)SocketChannel和ServerSocketChannel完成套接字通道實(shí)現(xiàn)。非阻塞/阻塞,同步,避免TCP建立連接使用三次握手帶來(lái)的開(kāi)銷(xiāo)。
3 AIO模型中通過(guò)AsynchronousSocketChannel和AsynchronousServerSocketChannel完成套接字通道實(shí)現(xiàn)。非阻塞,異步。
BIO NIO AIO 對(duì)比
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/101961.html
摘要:后改良為用線程池的方式代替新增線程,被稱(chēng)為偽異步。最大的問(wèn)題是阻塞,同步。每次請(qǐng)求都由程序執(zhí)行并返回,這是同步的缺陷。這些都會(huì)被注冊(cè)在多路復(fù)用器上。多路復(fù)用器提供選擇已經(jīng)就緒狀態(tài)任務(wù)的能力。并沒(méi)有采用的多路復(fù)用器,而是使用異步通道的概念。 Netty是一個(gè)提供異步事件驅(qū)動(dòng)的網(wǎng)絡(luò)應(yīng)用框架,用以快速開(kāi)發(fā)高性能、高可靠的網(wǎng)絡(luò)服務(wù)器和客戶(hù)端程序。Netty簡(jiǎn)化了網(wǎng)絡(luò)程序的開(kāi)發(fā),是很多框架和公司...
摘要:當(dāng)操作系統(tǒng)發(fā)生事件,并且準(zhǔn)備好數(shù)據(jù)后,在主動(dòng)通知應(yīng)用程序,觸發(fā)相應(yīng)的函數(shù)。當(dāng)失敗時(shí)觸發(fā)該方法,第一個(gè)參數(shù)代表操作失敗引發(fā)的異常或錯(cuò)誤。 BIO編程 回顧下Linux下阻塞IO模型: showImg(https://segmentfault.com/img/bVbtFcN?w=826&h=471); 再看看Java的BIO編程模型: showImg(https://segmentfaul...
摘要:一個(gè)多路復(fù)用器可以負(fù)責(zé)成千上萬(wàn)的通道,沒(méi)有上限。不需要通過(guò)對(duì)多路復(fù)用器對(duì)注冊(cè)的通道進(jìn)行輪詢(xún)操作即可實(shí)現(xiàn)異步讀寫(xiě),從而簡(jiǎn)化編程模型。同時(shí),支持支持如果是怎么辦最后,到目前位置,支持不支持二無(wú)法擴(kuò)展作為的核心,無(wú)法擴(kuò)展,私有構(gòu)造函數(shù)。 前言 netty 學(xué)習(xí) 基于 netty in action 5th, 官網(wǎng)資料,網(wǎng)絡(luò)博客等 1.1 Why Netty? netty 是一個(gè)中間層的抽象 ...
摘要:即可以理解為,方法都是異步的,完成后會(huì)主動(dòng)調(diào)用回調(diào)函數(shù)。主要在包下增加了下面四個(gè)異步通道其中的方法,會(huì)返回一個(gè)帶回調(diào)函數(shù)的對(duì)象,當(dāng)執(zhí)行完讀取寫(xiě)入操作后,直接調(diào)用回調(diào)函數(shù)。 本文原創(chuàng)地址,我的博客:jsbintask.cn/2019/04/16/…(食用效果最佳),轉(zhuǎn)載請(qǐng)注明出處! 在理解什么是BIO,NIO,AIO之前,我們首先需要了解什么是同步,異步,阻塞,非阻塞。假如我們現(xiàn)在要去銀行取...
閱讀 853·2021-11-24 09:38
閱讀 1085·2021-10-08 10:05
閱讀 2577·2021-09-10 11:21
閱讀 2800·2019-08-30 15:53
閱讀 1827·2019-08-30 15:52
閱讀 1964·2019-08-29 12:17
閱讀 3418·2019-08-29 11:21
閱讀 1609·2019-08-26 12:17