-
listen函數僅由TCP服務器調用,它做兩件事:
- 當socket函數創建一個套接字時,它被假設為一個主動套接字,也就是說,它是一個將調用connect發起連接的客戶套接字。listen函數把一個未連接的套接字轉換為一個被動套接字,指示內核應該接受指向該套接字的連接請求。根據TCP狀態轉換圖,調用listen導致套接字從CLOSED狀態轉換到LISTEN狀態。
- listen函數的第二個參數規定了內核應該為相應套接字排隊的最大連接個數:
#include<sys/socket.h> int listen(int sockfd, int backlog); 返回:若成功則為0,若出錯則為-1
-
為了理解其中的backlog參數,我們必須認識到內核為任何一個給定的監聽套接字維護兩個隊列:
- 未完成連接隊列,每個這樣的SYN分節對應其中一項:已由某個客戶發出並到達服務器,而服務器正在等待完成相應的TCP三路握手過程。這些套接字處於SYN_RECV狀態
- 已完成連接隊列,每個已完成TCP三路握手過程的客戶對應其中一項。這些套接字處於ESTABLISHED狀態。
-
下圖描繪了監聽套接字的兩個隊列
-
每當在未完成連接隊列中創建一項時,來自監聽套接字的參數就復制到即將建立的連接中,連接的創建機制是完全自動的。無需服務器進程插手。下圖展示了用這兩個隊列建立連接時所交換的分組:
-
當來自客戶的SYN到達時,TCP在未完成連接隊列中創建一個新項,然后響應以三路握手的第二個分節:服務器的SYN響應,其中捎帶對客戶SYN的ACK。這一項一直保留在未完成連接隊列中,直到三路握手的第三個分節(客戶對服務器的SYN的ACK)到達或者該項超時為止。
-
如果三路握手正常完成,該項從未完成連接隊列移到已完成連接隊列的隊尾。當進程調用accept時,已完成連接隊列中的隊頭項將返回給進程,或者該隊列為空,那么進程就被投入睡眠,直到TCP在該隊列中放入一項才喚醒它。