在這里使用 java代碼,記錄 BIO 的理解。要使用 jdk1.4 版本的才能在追蹤的時(shí)候看出本質(zhì)。高版本的jdk 內(nèi)部已經(jīng)有了優(yōu)化,會(huì)使用poll的方式來(lái)執(zhí)行。Block IO。
在linux中使用 strace 命令,可以追蹤程序的系統(tǒng)調(diào)用。

追蹤示例.png
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class SocketBIO {
public static void main(String[] args) throws IOException {
ServerSocket server = new ServerSocket(9090, 20);
System.out.println("服務(wù)已開啟");
while (true) {
Socket client = server.accept();//阻塞1,等待接收連接
System.out.println("客戶端已連上,端口號(hào):" + client.getPort());
//一旦獲取到了一個(gè)client,分一個(gè)線程出去處理
new Thread(() -> {
InputStream in = null;
try {
in = client.getInputStream();//獲取輸入流,字節(jié)
//將字節(jié)轉(zhuǎn)換為字符并讀取
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(in));
while (true) {
String s = bufferedReader.readLine();//阻塞2,獲取該線程連接的輸入
if (null != s) {
System.out.println("輸入的內(nèi)容是:" + s);
} else {
client.close();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
}
}
以上代碼執(zhí)行后,會(huì)有兩個(gè)阻塞,線程一直被掛起,等待連接和輸入
用strace追蹤后可以發(fā)現(xiàn)下圖是主線程等待連接的阻塞:

主線程的阻塞.png
開啟一個(gè)socket,返回一個(gè)
變量3,為3綁定端口號(hào),然后監(jiān)聽,做write系統(tǒng)調(diào)用,3這個(gè)線程開始等待連接。上圖中的
2531行中的write函數(shù)就是系統(tǒng)調(diào)用,參數(shù)和上面代碼的輸出不一致。當(dāng)使用
nc命令連接上這個(gè)服務(wù)后,將會(huì)返回一個(gè)客戶端,進(jìn)入線程中,主線程繼續(xù) accept。從追蹤命令當(dāng)中可以看到,
java當(dāng)中的新啟動(dòng)一個(gè)線程,在linux中,實(shí)際上是開啟了一個(gè)進(jìn)程。新進(jìn)程是從主進(jìn)程中clone出來(lái)的。
新線程的阻塞.png
返回來(lái)個(gè)1590進(jìn)程號(hào)。
繼續(xù)追蹤1590進(jìn)程號(hào)可以發(fā)現(xiàn)還有一個(gè)recv阻塞了。對(duì)應(yīng)的是代碼的這一行bufferedReader.readLine()。

總結(jié).png
這里的變量5是新線程的client。

image.png
- 因?yàn)?code>BIO模式有
blocking,所以只能以拋出線城的方式去處理連接,不然在主線程只能接收一個(gè)連接。程序員的代碼是無(wú)法解決這個(gè)問(wèn)題的,因?yàn)橄胍B接多個(gè),必然只能拋出多個(gè)線程,因?yàn)橛凶枞?/li> - 每輪一下,還會(huì)涉及到系統(tǒng)調(diào)用,
accept,clone,recv都是系統(tǒng)調(diào)用函數(shù)。所以要解決這個(gè)問(wèn)題,只能從內(nèi)核下手。所以有了后來(lái)的NIO,noblock。
以上兩點(diǎn)就是BIO的弊端