TCP- 半連接隊列 -全連接隊列


image.png

1. SYN_REVD, ESTABELLISHED 狀態對應的隊列

TCP 建立連接時要經過 3 次握手,在客戶端向服務器發起連接時,
對於服務器而言,一個完整的連接建立過程,服務器會經歷 2 種 TCP 狀態:SYN_REVD, ESTABELLISHED。

對應也會維護兩個隊列:

  1. 一個存放 SYN_REVD狀態的連接 隊列(半連接隊列)
  2. 一個存放ESTABLISHED狀態但是還未被accept的連接隊列。
    如果一個服務器要處理大量網絡連接,且並發性比較高,那么這兩個隊列長度就非常重要了。因為,即使服務器的硬件配置非常高,服務器端程序性能很好,
    但是這兩個隊列非常小,那么經常會出現客戶端連接不上的現象,
    因為這兩個隊列一旦滿了后,很容易丟包,或者連接被復位。
    所以,如果服務器並發訪問量非常高,那么這兩個隊列的設置就非常重要了。

2. 半連接隊列

2.1 半連接隊列長度如何計算

半連接隊列長度由內核參數 tcp_max_syn_backlog 決定,
當使用 SYN Cookie 時(就是內核參數 net.ipv4.tcp_syncookies = 1),這個參數無效,
半連接隊列長度 = min(backlog, 內核參數 net.core.somaxconn,內核參數 tcp_max_syn_backlog),半連接隊列的長度肯定小於全連接隊列的長度
這個公式實際上規定半連接隊列長度不能超過全連接隊列長度。

2.2 半連接滿之后的動作

首先,全連接滿會影響半連接滿。全連接滿而且半連接中有一定數目處於SYN_REVD狀態的連接時,沒有必要再繼續新的半連接,因為此時全連接已滿,此時的動作是直接忽略該連接。
半連接滿了之后的動作是直接忽略(ignore or dropped),此時客戶端需要不斷的重發SYNC進行重試,重試的參數由tcp_syn_retries決定,該參數默認是5。如果超過客戶端設置的超時時間,會報連接超時異常


2.3 syn flood攻擊

客戶端發出SYNC之后,不響應ACK,此時造成半連接隊列滿,server不能再提供服務,正常的客戶端一直報連接超時。
為了應對該攻擊,有兩種辦法:

  1. tcp_syncookies
    不建議該方案,因為違背了TCP規范
  2. 其他參數
    2.1 tcp_synack_retries 減小重傳次數
    2.2 tcp_max_syn_backlog 調整半連接隊列大小

2.4 怎么監控半連接滿了

667399 SYNs to LISTEN sockets ignored表明已經忽略SYN次了,此時說明半連接隊列滿了,或者因為全連接滿而影響了半連接的進行。

[root@server ~]#  netstat -s | egrep "listen|LISTEN" 
667399 times the listen queue of a socket overflowed
667399 SYNs to LISTEN sockets ignored

3. 全連接隊列

3.1 全連接隊列長度如何計算

全連接隊列長度 = min(backlog, 內核參數 net.core.somaxconn)net.core.somaxconn 默認為 128
這個很好理解,net.core.somaxconn 定義了系統級別的全連接隊列最大長度,
backlog 只是應用層傳入的參數,不可能超過內核參數,所以 backlog 必須小於等於 net.core.somaxconn。

3.1.1 backlog-應用參數

對於 Linux 而言,基本上任意語言實現的通信框架或服務器程序在構造 socket server 時,都提供了 backlog 這個參數,
因為在監聽端口時,都會調用系統底層 API: int listen(int sockfd, int backlog);
listen 函數中 backlog 參數的定義如下:

Now it specifies the queue length for completely established sockets waiting to be accepted,
instead of the number of incomplete connection requests.
The maximum length of the queue for incomplete sockets can be set using the tcp_max_syn_backlog sysctl.
When syncookies are enabled there is no logical maximum length and this sysctl setting is ignored.
If the socket is of type AF_INET, and the backlog argument is greater than the constant SOMAXCONN(128 default),
it is silently truncated to SOMAXCONN.
backlog 參數描述的是服務器端 TCP ESTABELLISHED 狀態對應的全連接隊列長度。

3.1.2 net.core.somaxconn-內核參數

cat /proc/sys/net/core/somaxconn或者sysctl -a | grep "net.core.somaxconn"
線上機器(LINK)的運行結果如下:


 
image.png

 
image.png

3.2 查看全連接長度

Recv-Q:全連連隊列中數據的個數,也就是等待被accept的個數。
Send-Q:全連接隊列長度


 
image.png

3.3 全連接隊列滿之后的動作

  1. 全連接隊列滿了之后,收到第三次握手的ACK之后首先根據tcp_abort_on_overflow來執行滿之后的的動作,該參數默認為0。
    0表示:如果三次握手第三步的時候全連接隊列滿了那么server扔掉client 發過來的ack(在server端認為連接還沒建立起來)
    1表示:第三步的時候如果全連接隊列滿了,server發送一個reset包給client,表示廢掉這個握手過程和這個連接(本來在server端這個連接就還沒建立起來)。
  2. 如果tcp_abort_on_overflow為0,則srver直接忽略收到的ACK,之后server端會不斷的重發SYNC+ACK。其中tcp_synack_retries參數決定了重試幾次才放棄。tcp_synack_retries的默認值是5,重試的間隔時間從1s開始,下次的重試間隔時間是前一次的雙倍,5次的重試時間間隔為1s, 2s, 4s, 8s, 16s,總共31s,第5次發出后還要等32s都知道第5次也超時了,所以,總共需要 1s + 2s + 4s+ 8s+ 16s + 32s = 63s,TCP才會把斷開這個連接。
    如果client走完第三步在client看來連接已經建立好了,但是server上的對應連接實際沒有准備好,這個時候如果client發數據給server,server會怎么處理呢?(有同學說會reset,還是實踐看看)
    先來看一個例子:
     
    image.png

    如上圖,150166號包是三次握手中的第三步client發送ack給server,然后150167號包中client發送了一個長度為816的包給server,因為在這個時候client認為連接建立成功,但是server上這個連接實際沒有ready,所以server沒有回復,一段時間后client認為丟包了然后重傳這816個字節的包,一直到超時,client報連接異常並主動發fin包斷開該連接。
    這個問題也叫client fooling,可以看這里:https://github.com/torvalds/linux/commit/5ea8ea2cb7f1d0db15762c9b0bb9e7330425a071
    從上面的實際抓包來看不是reset,而是server忽略這些包,然后client重傳,一定次數后client認為異常,然后斷開連接。
  3. 如果tcp_abort_on_overflow為1,則直接響應RST信號,此時客戶端報connection reset by peer。

3.4 怎么監控全連接隊列滿了

  1. 通過netstat
    667399 times the listen queue of a socket overflowed 說明出現了667399次全連接滿的情況
[root@server ~]#  netstat -s | egrep "listen|LISTEN" 
667399 times the listen queue of a socket overflowed
667399 SYNs to LISTEN sockets ignored
  1. 通過ss
    Recv-Q表明了當前當前全連接隊列使用了11,超過了最大值10(Send-Q所指示的)
Fri May  5 13:50:23 CST 2017
Recv-Q Send-QLocal Address:Port  Peer Address:Port
11         10         *:3306               *:*

4 參數

  1. tcp_max_syn_backlog
  2. net.core.somaxconn
  3. backlog(應用層傳入),java默認為50,LINK線上機器默認為128。
  4. tcp_syncookies
  5. tcp_synack_retries
  6. tcp_abort_on_overflow
  7. tcp_syn_retries

5. 容器中的全連接隊列參數

tomcat默認短連接,backlog(在Tomcat里面的術語是Accept count)默認100.

#ss -lnt
Recv-Q Send-Q   Local Address:Port Peer Address:Port 0 100 *:8080 *:* 

Nginx默認是511

$sudo ss -lnt State Recv-Q Send-Q Local Address:PortPeer Address:Port LISTEN 0 511 *:8085 *:* LISTEN 0 511 *:8085 *:* 

因為Nginx是多進程模式,也就是多個進程都監聽同一個端口以盡量避免上下文切換來提升性能



 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM