聲明:本文來自網絡博文的合並,文后有鏈接。
一、listen函數僅由TCP服務器調用
它做兩件事:
1、當socket函數創建一個套接字時,它被假設為一個主動套接字,也就是說,它是一個將調用connect發起連接的客戶套接字。listen函數把一個未連接的套接字轉換為一個被動套接字,指示內核應該接受指向該套接字的連接請求。根據TCP狀態轉換圖,調用listen導致套接字從CLOSED狀態轉換到LISTEN狀態。
2、listen函數的第二個參數規定了內核應該為相應套接字排隊的最大連接個數:
1 #include<sys/socket.h> 2 int listen(int sockfd, int backlog); 3 返回:若成功則為0,若出錯則為-1
為了理解其中的backlog參數,我們必須認識到內核為任何一個給定的監聽套接字維護兩個隊列:
(1)未完成連接隊列,每個這樣的SYN分節對應其中一項:已由某個客戶發出並到達服務器,而服務器正在等待完成相應的TCP三路握手過程。這些套接字處於SYN_RECV狀態
(2)已完成連接隊列,每個已完成TCP三路握手過程的客戶對應其中一項。這些套接字處於ESTABLISHED狀態。
下圖描繪了監聽套接字的兩個隊列
每當在未完成連接隊列中創建一項時,來自監聽套接字的參數就復制到即將建立的連接中,連接的創建機制是完全自動的。無需服務器進程插手。下圖展示了用這兩個隊列建立連接時所交換的分組:
當來自客戶的SYN到達時,TCP在未完成連接隊列中創建一個新項,然后響應以三路握手的第二個分節:服務器的SYN響應,其中捎帶對客戶SYN的ACK。這一項一直保留在未完成連接隊列中,直到三路握手的第三個分節(客戶對服務器的SYN的ACK)到達或者該項超時為止。
如果三路握手正常完成,該項從未完成連接隊列移到已完成連接隊列的隊尾。當進程調用accept時,已完成連接隊列中的隊頭項將返回給進程,或者該隊列為空,那么進程就被投入睡眠,直到TCP在該隊列中放入一項才喚醒它。
二、總結:
1、accept()函數不參與三次握手,而只負責從建立連接隊列中取出一個連接和socketfd進行綁定;
2、backlog參數決定了未完成隊列和已完成隊列中連接數目之和的最大值(從內核的角度,是否這個和就是等於sock->recv_queue ?);
3、accept()函數調用,會從已連接隊列中取出一個“連接”(可以說描述的數據結構listensocket->sock->recv_queue[sk_buff] ? ),未完成隊列和已完成隊列中連接目之和將減少1;即accept將監聽套接字對應的sock的接收隊列中的已建立連接的sk_buff取下(從該sk_buff中可以獲得對端主機的發送過來的tcp/ip數據包)
4、 監聽套接字的已完成隊列中的元素個數大於0,那么該套接字是可讀的。
5、 當程序調用accept的時候(設置阻塞參數),那么判定該套接字是否可讀,不可讀則進入睡眠,直至已完成隊列中的元素個數大於0(監聽套接字可讀)而喚起監聽進程)
本文僅用於個人學習。
Ref:
http://www.cnblogs.com/chris-cp/p/4022262.html
http://www.cnblogs.com/lengender-12/archive/2017/05/05/6813057.html