TCP/IP網絡編程深入認識

TCP/IP網絡編程深入認識

  • TCP網絡編程中connect()、listen()和accept()三者之間的關係

基於 TCP 的網絡編程開發分爲服務器端和客戶端兩部分,常見的核心步驟和流程如下:

  • connect函數:對於客戶端的 connect() 函數,該函數的功能爲客戶端主動連接服務器,建立連接是通過三次握手,而這個連接的過程是由內核完成,不是這個函數完成的,這個函數的作用僅僅是通知 Linux 內核,讓 Linux 內核自動完成 TCP 三次握手連接,最後把連接的結果返回給這個函數的返回值(成功連接爲0, 失敗爲-1)。通常的情況,客戶端的 connect() 函數默認會一直阻塞,直到三次握手成功或超時失敗才返回(正常的情況,這個過程很快完成)。
  • listen函數:對於服務器,它是被動連接的。listen() 函數的主要作用就是將套接字( sockfd )變成被動的連接監聽套接字(被動等待客戶端的連接),至於參數 backlog 的作用是設置內核中連接隊列的長度(這個長度有什麼用,後面做詳細的解釋),TCP 三次握手也不是由這個函數完成,listen()的作用僅僅告訴內核一些信息。 

#include<sys/socket.h>

int listen(int sockfd, int backlog);

這裏需要注意的是,listen()函數不會阻塞,它主要做的事情:將該套接字和套接字對應的連接隊列長度告訴 Linux 內核,然後,listen()函數就結束。這樣的話,當有一個客戶端主動連接(connect()),Linux 內核就自動完成TCP 三次握手,將建立好的鏈接自動存儲到隊列中,如此重複。所以,只要 TCP 服務器調用了 listen(),客戶端就可以通過 connect() 和服務器建立連接,而這個連接的過程是由內核完成。

listen函數中的backlog參數作用:告訴內核連接隊列的長度(即爲兩個隊列的長度)。

內核爲任何一個給定的監聽套接口維護兩個隊列:1. 未完成連接隊列(incomplete connection queue),每個這樣的 SYN 分節對應其中一項:已由某個客戶發出併到達服務器,而服務器正在等待完成相應的 TCP 三次握手過程。這些套接口處於 SYN_RCVD 狀態。2. 完成連接隊列(completed connection queue),每個已完成 TCP 三次握手過程的客戶對應其中一項。這些套接口處於 ESTABLISHED 狀態。

當來自客戶的 SYN 到達時,TCP 在未完成連接隊列中創建一個新項,然後響應以三次握手的第二個分節:服務器的 SYN 響應,其中稍帶對客戶 SYN 的 ACK(即SYN+ACK),這一項一直保留在未完成連接隊列中,直到三次握手的第三個分節(客戶對服務器 SYN 的 ACK )到達或者該項超時爲止。如果三次握手正常完成,該項就從未完成連接隊列移到已完成連接隊列的隊尾。

  • accpet函數:accept()函數功能是,從處於 established 狀態的連接隊列頭部取出一個已經完成的連接,如果這個隊列沒有已經完成的連接,accept()函數就會阻塞,直到取出隊列中已完成的用戶連接爲止。

如果,服務器不能及時調用 accept() 取走隊列中已完成的連接,隊列滿掉後會怎樣呢?,例如等所有客戶端調用connect完畢後,服務器再調用accept函數——出現情況:Unix服務器的連接隊列滿掉後,服務器不會對再對建立新連接的syn進行應答,所以客戶端的 connect 就會返回 ETIMEDOUT。但實際上Linux的並不是這樣的!TCP 的連接隊列滿後,Linux 不會如書中所說的拒絕連接,只是有些會延時連接,有的 connect()立刻成功返回了。對於服務器 accpet() 函數也是這樣的結果:有的立馬成功返回,有較大延遲後成功返回。而且accept()未必能把已經建立好的連接全部取出來(如:當隊列的長度指定爲 0 )

故寫程序時服務器的 listen() 的第二個參數最好還是根據需要填寫,寫太大不好(具體可以看cat /proc/sys/net/core/somaxconn,默認最大值限制是 128),浪費資源,寫太小也不好,延時建立連接。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章