網絡通信 IO 模型 - BIO

承接上文網絡通信 IO 模型上

BIO的Java代碼

服務端創建一個 ServerSocket,綁定了端口號 8090,目的是讓客戶端和服務端建立連接後進行通信,然後進入死循環,死循環裏面會調用 server.accept 得到一個 socket 客戶端,打印客戶端的端口號,然後啓動一個線程,爲什麼要啓動一個線程?

因爲 accpet 會阻塞,如果因爲沒有客戶端建立連接,就沒有返回值,會一直阻塞,只有客戶端建立連接,才能從阻塞變成返回,然後再讀取客戶端發送過來的數據,讀取輸入流並打印用戶發送的數據,讀取的話,也有可能變成阻塞狀態,如果客戶端一直不發送數據過來,服務器端就會一直阻塞,所以要把客戶端讀取的過程放到另外一個線程裏面去做。

通過strace命令追蹤進程情況

strace -ff -o out java TestSocket

通過 strace 命令追蹤 java 程序有多少個線程,每個線程對內核產生哪些系統調用,都會記錄下來。

啓動該程序,首先會打印表示第一步創建一個 ServerSocket,綁定了 8090 端口,然後進入阻塞狀態,直到有客戶端建立連接。

這時多了以 out 開頭、數值結尾的 8 個文件,代表 8 個線程。

當有一個客戶端連接的時候,會 new 一個新線程,就變成 9 個 out 文件了。

一個 java 程序剛啓動的時候,它自身就是多線程的,有些線程負責 gc 回收,有些線程負責監控客戶端建立的 socket 連接等。

上圖中的主線程是 8211,

查看 out.8211 就可以看到 java 程序在運行時和內核交互的整個過程。

每一行最前面代表系統調用的內核函數名稱,接着是入參,最後是返回值。

先看這個文件的最後一行,accept 阻塞在這裏了,一直在等待客戶端的連接。

首先系統調用 socket 方法得到一個文件描述符 3 即對應 ServerSocket,將 3 綁定到 8090 端口上,綁定完之後,下面 listen 表示開始監聽 3,其實是在監聽 8090 這個端口。

從阻塞到不阻塞的過程是怎樣的?

啓動 TestSocket 程序,進程編號是 8211,主線程編號也是 8211,監聽端口是 8090,目標地址可以是任何地址和任何端口。

用nc模擬一個客戶端建立連接

nc localhost 8090

nc(net connection 或 net cat) 就想象成一個網絡通信的客戶端,它能幫你完成 tcp 的三次握手。

TestSocket 程序就會打印一個客戶端連接進來了,

表示客戶端申請了一個隨機端口號連接到了服務端 8090,

剛纔 accept 3 阻塞在這裏了,一個客戶端連接進來之後,就會立刻返回一個代表客戶端連接的文件描述符 5,並且綁定了客戶端端口號,

現在多了一個連接狀態,目標程序是本地服務(127.0.0.1)的 8090 端口,來自於本地的 59033 這個客戶端程序,

59033 正好對應 nc 進程。

TestSocket 程序從開始只有一個文件描述符 3,然後一個客戶端建立了連接得到了文件描述符 5,然後 Java 主線程啓動一個子線程讀取文件描述符 5.

如何啓動一個新的線程?

調用 clone 這個內核的系統調用創建了一個新的線程 8281。

java 的線程其實就是調用內核的系統調用 clone,然後得到一個 8281 線程,這個線程同屬於最開始的進程 id 8211,

8211 代表一個線程組。

此時又多了一個 8281 的線程文件,查看該文件

調用了內核的 recv 方法,文件描述符 5 是在主線程產生的一個客戶端 socket 連接,用另外一個線程去讀文件描述符 5。

主線程繼續 accpet。

每多一個連接,就多了一個線程,如果有一萬個連接,就會有一萬個線程。

整體過程

先調用 socket 返回 fd 3,將 fd 3 綁定 8090 端口,然後監聽 8090 端口。

while 死循環,先 accept 接收客戶端的連接,如果沒有連接就一直阻塞,如果有連接,則讀取連接,如果客戶端一直沒有發送數據過來,就會一直阻塞,所以爲了不影響主線程接收連接,再創建一個新的線程,用於讀取每個客戶端發送過來的數據。

這是最古老的網絡 IO 模型 BIO。

一個線程對應一個連接,優勢是可以接受很多連接。

弊端:如果進程特別多或線程特別多的時候,就會造成內存的浪費,cpu 調度也會消耗了更多 cpu 的時間片。

BIO 中有 2 個方法是阻塞(BLOCKING)的,一個是主線程的 accept,另一個是 recv 或 read 這 2 個方法。

這 2 個系統調用會阻塞,所以不能把都有可能阻塞的操作放到一個線程裏 ,否則一個阻塞就會干預到另外一個阻塞,所以解決 BIO 模型的弊端 ,只需要非阻塞(NON BLOCKING)就可以了,非阻塞網絡模型就是 NIO 了。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/uDM9sENKUcJ50_5ySSrPfg