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