一文理清什么是BIO以及如何使用
一、BIO概述
BIO(Blocking I/O)是傳統java io編程既同步阻塞IO,服務器實現模式為一個連接一個線程。客戶端有連接請求時服務器端就會新起一個線程進行處理。當線程空閑時為減少不必要的線程開銷,可以通過線程池機制改善。BIO方式適合用于連接數目比較小且固定的架構,這種方式對服務器資源要求比較高,并發(fā)局限應用中。
二、BIO工作機制
客戶端
- 通過Socket對象請求與服務端建立連接。
- 從Socket中得到字節(jié)輸入或者字節(jié)輸出流進行數據讀寫操作。
服務端
- 通過ServerSocket注冊端口。
- 服務端通過調用accept方法用于監(jiān)聽客戶端的Socket請求。
- 從Socket中得到字節(jié)輸入或者字節(jié)輸出流進行數據讀寫操作。
三、同步阻塞步驟
- 服務端啟動一個ServerSocket
- 客戶端啟動Socket對服務器進行通信,默認情況下服務器端需要對每個客戶建立一個線程與之通訊。
- 客戶端發(fā)出請求后,先咨詢服務器是否有線程響應,如果沒有則會等待,或者被拒絕。
- 如果有響應,客戶端線程會等待請求結束后,在繼續(xù)執(zhí)行。
四、編碼實現傳統BIO
- 傳統的同步阻塞模型開發(fā)中,服務端ServerSocket負責綁定IP地址,啟動監(jiān)聽端口;客戶端Socket負責 發(fā)起 連接操作。連接成功后,雙方通過輸入和輸出流進行同步阻塞式通信。
- 基于BIO模式下的通信,客戶端-服務端是完全同步,完全藕合的。
服務端代碼
public static void main(String[] args) { System.out.println("===服務端啟動==="); ServerSocket serverSocket=null; try { serverSocket=new ServerSocket(5000); Socket socket=serverSocket.accept(); InputStream is = socket.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(is)); String msg; if ((msg = br.readLine()) != null) { System.out.println("服務端接收客戶端信息為:" + msg); } }catch (Exception exception){ System.out.println(exception.getMessage()); } }
客戶端代碼
public static void main(String[] args) { Socket socket = null; try { socket = new Socket("127.0.0.1",5000); OutputStream os = socket.getOutputStream(); PrintStream ps = new PrintStream(os); ps.println("Hi BIO! 與服務端通信成功"); ps.flush(); } catch (IOException e) { e.printStackTrace(); } }
總結
傳統BIO服務端會一直等待客戶端的消息,如果客戶端沒有進行消息的發(fā)送,服務端將一直進入阻塞狀態(tài),同時服務端是按照行獲取消息的,這意味著客戶端也必須按照行進行消息的發(fā)送,否則服務端將進 入等待消息的阻塞狀態(tài)。
五、BIO編程現實多發(fā)多收
BIO簡單方式是一個接收一個發(fā)送,如果實現多發(fā)多接收,下面將驗證多發(fā)多收的代碼
服務端代碼
public static void main(String[] args) { System.out.println("===服務端啟動==="); try { ServerSocket ss = new ServerSocket(9988); Socket socket = ss.accept(); InputStream is = socket.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(is)); String message; while ((message = br.readLine()) != null){ System.out.println("服務端接收客戶端信息為:" + message); } } catch (IOException e) { e.printStackTrace(); } }
客戶端代碼
public static void main(String[] args) { try { Socket socket = new Socket("localhost",9988); OutputStream os = socket.getOutputStream(); PrintStream ps = new PrintStream(os); Scanner scanner = new Scanner(System.in); while (true){ System.out.println("請輸入:"); String input = scanner.nextLine(); ps.println(input); ps.flush(); } } catch (IOException e) { e.printStackTrace(); } }
六、BIO模擬客戶端服務端多對一
前面無論是一收一發(fā),還是多發(fā)多收都僅限一個客戶端,如何實現一個服務端對應多個客戶端哪,這個主要是更改服務端代碼
實現步驟
- 監(jiān)聽tcp端口
- while循環(huán)接收連接
- 對接收到的連接進行InputStream/OutputStream操作
服務端實現
public void listen() throws IOException { ServerSocket serverSocket = null; try { log.info("服務啟動監(jiān)聽"); serverSocket = new ServerSocket(9988); //循環(huán)接收到客戶端的連接 while (true) { Socket socket = serverSocket.accept(); //得到連接后,開啟一個線程處理連接 handleSocket(socket); } }finally { if(serverSocket != null){ serverSocket.close(); } } } private void handleSocket(Socket socket) { HandleSocket socketHandle = new HandleSocket(socket); new Thread(socketHandle).start(); }
public void run() { BufferedInputStream bufferedInputStream = null; BufferedOutputStream bufferedOutputStream = null; try { bufferedInputStream = new BufferedInputStream(socket.getInputStream()); byte[] bytes = new byte[1024]; int len ; if ((len = bufferedInputStream.read(bytes)) > -1) { String result = new String(bytes,0,len); System.out.println("本次接收到的結果:"+result); } System.out.println("回復信息給客戶端:"); bufferedOutputStream = new BufferedOutputStream(socket.getOutputStream()); String outString = Thread.currentThread().getName()+"接收到了"; bufferedOutputStream.write(outString.getBytes()); bufferedOutputStream.flush(); System.out.println("回復完成:"); } catch (IOException e) { System.out.println("異常:"e.getLocalizedMessage()); } finally { System.out.println("關閉數據流:"); try { if (bufferedInputStream != null) { bufferedInputStream.close(); } if (bufferedOutputStream != null) { bufferedOutputStream.close(); } }catch (IOException e){ System.out.println("請輸入:"e.getLocalizedMessage()); } } }
客戶端實現
1.與服務端建立連接
2.發(fā)送消息給服務端
3.接收服務端返回的消息
public void start() throws IOException { Socket socket = new Socket("127.0.0.1", 8081); String msg = "Hi,This is the BioClient"; //1.拿到輸出流 //2.對輸出流進行處理 System.out.println("請輸入:"+msg); BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(socket.getOutputStream()); byte[] bytes = msg.getBytes(); //3.輸出msg bufferedOutputStream.write(bytes); bufferedOutputStream.flush(); System.out.println("發(fā)送完畢."); System.out.println("開始接收到消息."); //4.對輸入流進行處理 BufferedInputStream bufferedInputStream = new BufferedInputStream(socket.getInputStream()); byte[] inBytes = new byte[1024]; int len; //5.讀取輸入流 if ((len = bufferedInputStream.read(inBytes)) != -1) { String result = new String(inBytes, 0, len); System.out.println("接收到的消息="+result); } //6.關閉輸入輸出流 bufferedOutputStream.close(); bufferedInputStream.close(); socket.close(); }
七、總結
- 每個Socket接收到,都會創(chuàng)建一個線程,線程的競爭、切換上下文影響性能;
- 每個線程都會占用棧空間和CPU資源;
- 并不是每個socket都進行l(wèi)O操作,無意義的線程處理;
- 客戶端的并發(fā)訪問增加時。服務端將呈現1:1的線程開銷,訪問量越大,系統將發(fā)生線程棧溢出, 線程創(chuàng)建失敗,最終導致進程宕機或者僵死,從而不能對外提供服務;
以上就是一文理清什么是BIO以及如何使用的詳細內容,更多關于Java BIO的資料請關注腳本之家其它相關文章!
相關文章
Spring Security自定義異常 AccessDeniedHandler不生效解決方法
本文主要介紹了Spring Security自定義異常 AccessDeniedHandler不生效解決方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-07-07SpringBoot?如何通過?Profile?實現不同環(huán)境下的配置切換
SpringBoot通過profile實現在不同環(huán)境下的配置切換,比如常見的開發(fā)環(huán)境、測試環(huán)境、生產環(huán)境,SpringBoot常用配置文件主要有?2?種:properties?文件和yml文件,本文給大家詳細介紹SpringBoot?通過?Profile?實現不同環(huán)境下的配置切換,感興趣的朋友一起看看吧2022-08-08如何使用IDEA2022.1?創(chuàng)建Spring?Boot項目
這篇文章主要介紹了如何使用IDEA2022.1?創(chuàng)建Spring?Boot項目,大家在使用idea開發(fā)工具時發(fā)現給以往的版本略微的不同,細心的小編在此記錄下,需要的朋友可以參考下2022-08-08解決Maven打包只有幾十K,運行報錯no main manifest attribute
這篇文章主要介紹了解決Maven打包只有幾十K,運行報錯no main manifest attribute問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-06-06