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