天天看點

Socket `accept queue is full ` 但是一個連接配接需要從SYN->ACCEPT

由于标題長度有限制,我把想要描述的問題再次描述下:

核心通常會為每一個

LISTEN

狀态的

Socket

維護兩個隊列:

1

accept

隊列:

listen()

函數第二個參數

BACKLOG

指定,表示已完成連接配接的隊列,等待被

accept

函數取走。

2

SYN

隊列:由

/proc/sys/net/ipv4/tcp_max_syn_backlog

指定,表示處于

SYN_RECV

狀态的隊列。

如果沒有概念,參考:

http://blog.csdn.net/yangbodong22011/article/details/60399728

現在的問題是:

如果

accept

隊列已經滿了,并且伺服器沒有使用

accept

函數取走任何連接配接,那麼當一個連接配接需要從

SYN隊列

移動到

accept

隊列時會發生什麼?

正确的解釋

答案來源:

http://veithen.github.io/2014/01/01/how-tcp-backlog-works-in-linux.html

下面的部分我翻譯自作者原文:

This case is handled by the

tcp_check_req

function in

net/ipv4/tcp_minisocks.c

, The relevant code reads:

這種情況由

net/ipv4/tcp_minisocks.c

中的

tcp_check_req

函數處理,相關代碼如下:

child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL);
     if (child == NULL)
         goto listen_overflow;
           
For IPv4, the first line of code will actually call tcp_v4_syn_recv_sock in net/ipv4/tcp_ipv4.c, which contains the following code:

對于IPv4,第一行代碼實際上将調用

net/ipv4/tcp_ipv4.c

中的

tcp_v4_syn_recv_sock

,其中包含以下代碼:

if (sk_acceptq_is_full(sk))
                goto exit_overflow;
           
We see here the check for the accept queue. The code after the

exit_overflow

label will perform some cleanup, update the

ListenOverflows

and

ListenDrops

statistics in

/proc/net/netstat

and then return

NULL

. This will trigger the execution of the

listen_overflow

code in

tcp_check_req

:

我們在這裡看到了對于

accept

隊列的檢查。

exit_overflow

的代碼将執行一些清理,更新

/proc/net/netstat

中的

ListenOverflows

ListenDrops

統計資訊,然後傳回

NULL

。 這将觸發在

tcp_check_req

中執行

listen_overflow

代碼:

listen_overflow:
        if (!sysctl_tcp_abort_on_overflow) {
                inet_rsk(req)->acked = ;
                return NULL;
        }
           
This means that unless /proc/sys/net/ipv4/tcp_abort_on_overflow is set to 1 , the implementation basically does… nothing!

這意味着隻要

/proc/sys/net/ipv4/tcp_abort_on_overflow

被設定為

1

,在上面的

if (!sysctl_tcp_abort_on_overflow)

判斷中為0,就不會執行下面代碼,也就是Linux的政策是 : 什麼都不幹!!!!

備注:

這裡的什麼都不幹對應的是:

回應RST包

,用戶端出現

connection reset by peer

To summarize, if the TCP implementation in Linux receives the ACK packet of the 3-way handshake and the accept queue is full, it will basically ignore that packet. At first, this sounds strange, but remember that there is a timer associated with the SYN RECEIVED state: if the ACK packet is not received (or if it is ignored, as in the case considered here), then the TCP implementation will resend the SYN/ACK packet (with a certain number of retries specified by /proc/sys/net/ipv4/tcp_synack_retries and using an exponential backoff algorithm).

總而言之,如果

Linux

中的

TCP

接收到3次握手的

ACK資料包

,并且接受隊列已滿,它将基本上忽略該資料包。 這聽起來很奇怪,但是記住有一個與SYN RECEIVED狀态相關聯的定時器:如果沒有收到

ACK

分組(在這裡我們考慮的情況是ACK被忽略,就和沒有收到是一樣的),則

TCP

将重新發送

SYN/ACK

資料包(由

/proc/sys/net/ipv4/tcp_synack_retries

指定次數)。

備注:我的電腦是5

Socket `accept queue is full ` 但是一個連接配接需要從SYN->ACCEPT
Since the TCP implementation on the client side gets multiple SYN/ACK packets, it will assume that the ACK packet was lost and resend it (see the lines with TCP Dup ACK in the above trace). If the application on the server side reduces the backlog (i.e. consumes an entry from the accept queue) before the maximum number of SYN/ACK retries has been reached, then the TCP implementation will eventually process one of the duplicate ACKs, transition the state of the connection from SYN RECEIVED to ESTABLISHED and add it to the accept queue. Otherwise, the client will eventually get a RST packet (as in the sample shown above).

由于用戶端收到多個

SYN/ACK

分組,它将假定

ACK

分組丢失并重新發送它。 如果在達到最大

SYN/ACK

重試次數之前,伺服器側的應用程式減少了

Accept隊列大小

(即

調用accept

來取接受隊列的條目),則

TCP

将最終處理

重複ACK

中的一個,轉變狀态從

SYN RECEIVED

ESTABLISHED

的連接配接,并将其添加到

accept

隊列。 否則,用戶端将最終收到

RST

分組。

實驗驗證

驗證環境:

RedHat 7

Linux version 3.10.0-514.el7.x86_64

驗證思路:

  • 模拟一個”忙”的伺服器,保證它的

    accept隊列被占滿

  • 再用用戶端連接配接。

使用

wireshark

抓包情況如下所示(在新的标簽頁打開圖檔):

Socket `accept queue is full ` 但是一個連接配接需要從SYN->ACCEPT
  • 前三個包是三次握手,143是用戶端,155是伺服器。
  • 但是155伺服器此時

    accept隊列

    是滿的,雖然143和它完成了三次握手,但是它仍然不能從

    SYN RECEIVED

    狀态轉換到

    ESTABLISHED

    狀态。
  • 此時由于

    SYN RECEIVED

    狀态的定時器機制,伺服器155沒有收到

    ACK

    分組的時候(實際上是直接扔掉了),會繼續給用戶端143發送

    SYN/ACK

    資料包,143以為伺服器沒有收到

    ACK

    ,然後回複ACK,這也就是”黑色”的包為什麼有10個的原因了,伺服器發送的

    SYN/ACK

    有5個,用戶端回複的

    ACK

    有5個,就是

    /proc/sys/net/ipv4/tcp_synack_retries

    定義的。

我的疑問:

按道理,用戶端最後是會收到伺服器端發送的

RST

資料包的。但是我不知道多長時間之後會發,于是我等了很久(2小時)。此時伺服器端通過

netstat

指令檢視已經沒有任何用戶端的連接配接資訊了,但是用戶端檢視和伺服器的連接配接卻是

ESTABLISHED

狀态。很奇怪,這樣的話用戶端什麼時候退出呢? 難道它一直卡在那裡?

ps : 重新測了以下,伺服器丢掉

SYN RECEIVED

狀态的用戶端時間大概是1分鐘,也就是用戶端連接配接過來,伺服器

Accept隊列滿

的話,大概一分鐘之後伺服器就會丢失這個連接配接。但是此時用戶端維持着一條

ESTABLISHED

狀态的連接配接~~,它到底什麼時候釋放呢?

繼續閱讀