一文理清什么是BIO以及如何使用
一、BIO概述
BIO(Blocking I/O)是傳統(tǒng)java io編程既同步阻塞IO,服務(wù)器實(shí)現(xiàn)模式為一個(gè)連接一個(gè)線(xiàn)程。客戶(hù)端有連接請(qǐng)求時(shí)服務(wù)器端就會(huì)新起一個(gè)線(xiàn)程進(jìn)行處理。當(dāng)線(xiàn)程空閑時(shí)為減少不必要的線(xiàn)程開(kāi)銷(xiāo),可以通過(guò)線(xiàn)程池機(jī)制改善。BIO方式適合用于連接數(shù)目比較小且固定的架構(gòu),這種方式對(duì)服務(wù)器資源要求比較高,并發(fā)局限應(yīng)用中。
二、BIO工作機(jī)制

客戶(hù)端
- 通過(guò)Socket對(duì)象請(qǐng)求與服務(wù)端建立連接。
- 從Socket中得到字節(jié)輸入或者字節(jié)輸出流進(jìn)行數(shù)據(jù)讀寫(xiě)操作。
服務(wù)端
- 通過(guò)ServerSocket注冊(cè)端口。
- 服務(wù)端通過(guò)調(diào)用accept方法用于監(jiān)聽(tīng)客戶(hù)端的Socket請(qǐng)求。
- 從Socket中得到字節(jié)輸入或者字節(jié)輸出流進(jìn)行數(shù)據(jù)讀寫(xiě)操作。
三、同步阻塞步驟
- 服務(wù)端啟動(dòng)一個(gè)ServerSocket
- 客戶(hù)端啟動(dòng)Socket對(duì)服務(wù)器進(jìn)行通信,默認(rèn)情況下服務(wù)器端需要對(duì)每個(gè)客戶(hù)建立一個(gè)線(xiàn)程與之通訊。
- 客戶(hù)端發(fā)出請(qǐng)求后,先咨詢(xún)服務(wù)器是否有線(xiàn)程響應(yīng),如果沒(méi)有則會(huì)等待,或者被拒絕。
- 如果有響應(yīng),客戶(hù)端線(xiàn)程會(huì)等待請(qǐng)求結(jié)束后,在繼續(xù)執(zhí)行。
四、編碼實(shí)現(xiàn)傳統(tǒng)BIO
- 傳統(tǒng)的同步阻塞模型開(kāi)發(fā)中,服務(wù)端ServerSocket負(fù)責(zé)綁定IP地址,啟動(dòng)監(jiān)聽(tīng)端口;客戶(hù)端Socket負(fù)責(zé) 發(fā)起 連接操作。連接成功后,雙方通過(guò)輸入和輸出流進(jìn)行同步阻塞式通信。
- 基于BIO模式下的通信,客戶(hù)端-服務(wù)端是完全同步,完全藕合的。
服務(wù)端代碼
public static void main(String[] args) {
System.out.println("===服務(wù)端啟動(dòng)===");
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("服務(wù)端接收客戶(hù)端信息為:" + msg);
}
}catch (Exception exception){
System.out.println(exception.getMessage());
}
}客戶(hù)端代碼
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! 與服務(wù)端通信成功");
ps.flush();
} catch (IOException e) {
e.printStackTrace();
}
}總結(jié)
傳統(tǒng)BIO服務(wù)端會(huì)一直等待客戶(hù)端的消息,如果客戶(hù)端沒(méi)有進(jìn)行消息的發(fā)送,服務(wù)端將一直進(jìn)入阻塞狀態(tài),同時(shí)服務(wù)端是按照行獲取消息的,這意味著客戶(hù)端也必須按照行進(jìn)行消息的發(fā)送,否則服務(wù)端將進(jìn) 入等待消息的阻塞狀態(tài)。
五、BIO編程現(xiàn)實(shí)多發(fā)多收
BIO簡(jiǎn)單方式是一個(gè)接收一個(gè)發(fā)送,如果實(shí)現(xiàn)多發(fā)多接收,下面將驗(yàn)證多發(fā)多收的代碼
服務(wù)端代碼
public static void main(String[] args) {
System.out.println("===服務(wù)端啟動(dòng)===");
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("服務(wù)端接收客戶(hù)端信息為:" + message);
}
} catch (IOException e) {
e.printStackTrace();
}
}客戶(hù)端代碼
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("請(qǐng)輸入:");
String input = scanner.nextLine();
ps.println(input);
ps.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}六、BIO模擬客戶(hù)端服務(wù)端多對(duì)一
前面無(wú)論是一收一發(fā),還是多發(fā)多收都僅限一個(gè)客戶(hù)端,如何實(shí)現(xiàn)一個(gè)服務(wù)端對(duì)應(yīng)多個(gè)客戶(hù)端哪,這個(gè)主要是更改服務(wù)端代碼
實(shí)現(xiàn)步驟
- 監(jiān)聽(tīng)tcp端口
- while循環(huán)接收連接
- 對(duì)接收到的連接進(jìn)行InputStream/OutputStream操作
服務(wù)端實(shí)現(xiàn)
public void listen() throws IOException {
ServerSocket serverSocket = null;
try {
log.info("服務(wù)啟動(dòng)監(jiān)聽(tīng)");
serverSocket = new ServerSocket(9988);
//循環(huán)接收到客戶(hù)端的連接
while (true) {
Socket socket = serverSocket.accept();
//得到連接后,開(kāi)啟一個(gè)線(xiàn)程處理連接
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("本次接收到的結(jié)果:"+result);
}
System.out.println("回復(fù)信息給客戶(hù)端:");
bufferedOutputStream = new BufferedOutputStream(socket.getOutputStream());
String outString = Thread.currentThread().getName()+"接收到了";
bufferedOutputStream.write(outString.getBytes());
bufferedOutputStream.flush();
System.out.println("回復(fù)完成:");
} catch (IOException e) {
System.out.println("異常:"e.getLocalizedMessage());
} finally {
System.out.println("關(guān)閉數(shù)據(jù)流:");
try {
if (bufferedInputStream != null) {
bufferedInputStream.close();
}
if (bufferedOutputStream != null) {
bufferedOutputStream.close();
}
}catch (IOException e){
System.out.println("請(qǐng)輸入:"e.getLocalizedMessage());
}
}
}客戶(hù)端實(shí)現(xiàn)
1.與服務(wù)端建立連接
2.發(fā)送消息給服務(wù)端
3.接收服務(wù)端返回的消息
public void start() throws IOException {
Socket socket = new Socket("127.0.0.1", 8081);
String msg = "Hi,This is the BioClient";
//1.拿到輸出流
//2.對(duì)輸出流進(jìn)行處理
System.out.println("請(qǐng)輸入:"+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("開(kāi)始接收到消息.");
//4.對(duì)輸入流進(jìn)行處理
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.關(guān)閉輸入輸出流
bufferedOutputStream.close();
bufferedInputStream.close();
socket.close();
}七、總結(jié)
- 每個(gè)Socket接收到,都會(huì)創(chuàng)建一個(gè)線(xiàn)程,線(xiàn)程的競(jìng)爭(zhēng)、切換上下文影響性能;
- 每個(gè)線(xiàn)程都會(huì)占用棧空間和CPU資源;
- 并不是每個(gè)socket都進(jìn)行l(wèi)O操作,無(wú)意義的線(xiàn)程處理;
- 客戶(hù)端的并發(fā)訪(fǎng)問(wèn)增加時(shí)。服務(wù)端將呈現(xiàn)1:1的線(xiàn)程開(kāi)銷(xiāo),訪(fǎng)問(wèn)量越大,系統(tǒng)將發(fā)生線(xiàn)程棧溢出, 線(xiàn)程創(chuàng)建失敗,最終導(dǎo)致進(jìn)程宕機(jī)或者僵死,從而不能對(duì)外提供服務(wù);
以上就是一文理清什么是BIO以及如何使用的詳細(xì)內(nèi)容,更多關(guān)于Java BIO的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Mybatis-Plus通過(guò)SQL注入器實(shí)現(xiàn)批量插入的實(shí)踐
本文主要介紹了Mybatis-Plus通過(guò)SQL注入器實(shí)現(xiàn)批量插入的實(shí)踐,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08
Spring Security自定義異常 AccessDeniedHandler不生效解決方法
本文主要介紹了Spring Security自定義異常 AccessDeniedHandler不生效解決方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07
SpringBoot?如何通過(guò)?Profile?實(shí)現(xiàn)不同環(huán)境下的配置切換
SpringBoot通過(guò)profile實(shí)現(xiàn)在不同環(huán)境下的配置切換,比如常見(jiàn)的開(kāi)發(fā)環(huán)境、測(cè)試環(huán)境、生產(chǎn)環(huán)境,SpringBoot常用配置文件主要有?2?種:properties?文件和yml文件,本文給大家詳細(xì)介紹SpringBoot?通過(guò)?Profile?實(shí)現(xiàn)不同環(huán)境下的配置切換,感興趣的朋友一起看看吧2022-08-08
Java POI讀取excel中數(shù)值精度損失問(wèn)題解決
這篇文章主要介紹了Java POI讀取excel中數(shù)值精度損失問(wèn)題解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04
如何使用IDEA2022.1?創(chuàng)建Spring?Boot項(xiàng)目
這篇文章主要介紹了如何使用IDEA2022.1?創(chuàng)建Spring?Boot項(xiàng)目,大家在使用idea開(kāi)發(fā)工具時(shí)發(fā)現(xiàn)給以往的版本略微的不同,細(xì)心的小編在此記錄下,需要的朋友可以參考下2022-08-08
解決Maven打包只有幾十K,運(yùn)行報(bào)錯(cuò)no main manifest attribute
這篇文章主要介紹了解決Maven打包只有幾十K,運(yùn)行報(bào)錯(cuò)no main manifest attribute問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-06-06
Java內(nèi)部類(lèi)_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
內(nèi)部類(lèi)是指在一個(gè)外部類(lèi)的內(nèi)部再定義一個(gè)類(lèi)。下面通過(guò)本文給大家java內(nèi)部類(lèi)的使用小結(jié),需要的朋友參考下吧2017-04-04
java調(diào)用ffmpeg實(shí)現(xiàn)視頻轉(zhuǎn)換的方法
這篇文章主要介紹了java調(diào)用ffmpeg實(shí)現(xiàn)視頻轉(zhuǎn)換的方法,較為詳細(xì)分析了java視頻格式轉(zhuǎn)換所需要的步驟及具體實(shí)現(xiàn)技巧,需要的朋友可以參考下2015-06-06

