概述
服務端提供 IP 和監聽端口,客戶端通過連接操作想服務端監聽的地址發起連接請求,通過三次握手連接,如果連接成功建立,雙方就可以通過套接字進行通信。
傳統的同步阻塞模型開發中,<strong>ServerSocket 負責綁定 IP 地址,啟動監聽端口;<strong>Socket 負責發起連接操作。連接成功後,雙方通過輸入和輸出流進行同步阻塞式通信。
傳統BIO 通信模型:採用BIO 通信模型的服務端,通常由一個獨立的 Acceptor線程負責監聽客戶端的連接,它接收到客戶端連接請求之後為每個客戶端創建一個新的線程進行鏈路處理沒處理完成後,通過輸出流返回應答給客戶端,線程銷燬。即典型的一請求一應答模型。
該模型最大的問題就是缺乏彈性伸縮能力,當客戶端併發訪問量增加後,服務端的線程個數和客戶端併發訪問數呈 1:1 的正比關係,Java 中的線程也是比較寶貴的系統資源,線程數量快速膨脹後,系統的性能將急劇下降,隨著訪問量的繼續增大,系統最終就 死- 掉- 了。
為了改進這種一連接一線程的模型,我們可以使用線程池來管理這些線程,實現1個或多個線程處理N個客戶端的模型(但是底層還是使用的同步阻塞I/O),通常被稱為“偽異步 I/O 模型“。
BIO通信代碼demo
服務端:
<code>package cn.pine.bio;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.net.InetSocketAddress;import java.net.ServerSocket;import java.net.Socket;/** *@author pine * *類說明:Bio通信的服務端 */public class Server { public static void main(String[] args) throws IOException { /*服務器必備*/ ServerSocket serverSocket = new ServerSocket(); /*綁定監聽端口*/ serverSocket.bind(new InetSocketAddress(10001)); System.out.println("Server start......."); while(true){ new Thread(new ServerTask(serverSocket.accept())).start(); } } private static class ServerTask implements Runnable{ private Socket socket = null; public ServerTask(Socket socket) { this.socket = socket; } @Override public void run() { /*拿和客戶端通訊的輸入輸出流*/ try(ObjectInputStream inputStream = new ObjectInputStream(socket.getInputStream()); ObjectOutputStream outputStream = new ObjectOutputStream(socket.getOutputStream())){ /*服務器的輸入*/ String userName = inputStream.readUTF(); System.out.println("Accept clinet message:"+userName); outputStream.writeUTF("Hello,"+userName); outputStream.flush(); }catch (Exception e){ e.printStackTrace(); } finally { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } } }}/<code>
客戶端:
<code>package cn.enjoyedu.bio;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.net.InetSocketAddress;import java.net.Socket;/** *@author pine * *類說明:Bio通信的客戶端 */public class Client { public static void main(String[] args) throws IOException { //客戶端啟動必備 Socket socket = null; //實例化與服務端通信的輸入輸出流 ObjectOutputStream output = null; ObjectInputStream input = null; //服務器的通信地址 InetSocketAddress addr = new InetSocketAddress("127.0.0.1",10001); try{ socket = new Socket(); /*連接服務器*/ socket.connect(addr); output = new ObjectOutputStream(socket.getOutputStream()); input = new ObjectInputStream(socket.getInputStream()); /*向服務器輸出請求*/ output.writeUTF("Mark"); output.flush(); //接收服務器的輸出 System.out.println(input.readUTF()); }finally{ if (socket!=null) socket.close(); if (output!=null) output.close(); if (input!=null) input.close(); } }}/<code>
偽異步IO代碼demo
服務端:
<code>package cn.enjoyedu.bio;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.net.InetSocketAddress;import java.net.ServerSocket;import java.net.Socket;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;/** *@author Mark老師 享學課堂 https://enjoy.ke.qq.com * *類說明:Bio通信的服務端 */public class ServerPool { private static ExecutorService executorService = Executors.newFixedThreadPool( Runtime.getRuntime().availableProcessors()); public static void main(String[] args) throws IOException { //服務端啟動必備 ServerSocket serverSocket = new ServerSocket(); //表示服務端在哪個端口上監聽 serverSocket.bind(new InetSocketAddress(10001)); System.out.println("Start Server ...."); try{ while(true){ executorService.execute(new ServerTask(serverSocket.accept())); } }finally { serverSocket.close(); } } //每個和客戶端的通信都會打包成一個任務,交個一個線程來執行 private static class ServerTask implements Runnable{ private Socket socket = null; public ServerTask(Socket socket){ this.socket = socket; } @Override public void run() { //實例化與客戶端通信的輸入輸出流 try(ObjectInputStream inputStream = new ObjectInputStream(socket.getInputStream()); ObjectOutputStream outputStream = new ObjectOutputStream(socket.getOutputStream())){ //接收客戶端的輸出,也就是服務器的輸入 String userName = inputStream.readUTF(); System.out.println("Accept client message:"+userName); //服務器的輸出,也就是客戶端的輸入 outputStream.writeUTF("Hello,"+userName); outputStream.flush(); }catch(Exception e){ e.printStackTrace(); }finally { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } } }}/<code>
客戶端同BIO一樣。
閱讀更多 JAVA破局之路 的文章