1. 程式人生 > >TCP3次握手和backlog溢位

TCP3次握手和backlog溢位

TCP3次握手,實際上可分為4步:

客戶端發起connect(),傳送SYN j

伺服器從SYN queue中建立條目,響應SYN k, ACK J+1

客戶端connect()成功返回,響應ACK K+1

伺服器將socketSYN queue移入accept queueaccept()成功返回

注:SYN/FIN各佔一個序列號,ACK/RST不佔序列號



SYN queue長度由
tcp_max_syn_backlog指定,accept queue則由net.core.somaxconn決定,listen(fd, backlog)backlog上限由

somaxconn決定;

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.

客戶端connect()返回不代表TCP連線建立成功,有可能此時伺服器accept queue已滿,OS會直接丟棄後續ACK請求;

客戶端以為連線已建立,開始後續呼叫(譬如send)等待直至超時;

伺服器則等待ACK超時,會重傳SYN k, ACK J+1給客戶端(重傳次數受限net.ipv4.tcp_synack_retries)

注:accept queue溢位,即便SYN queue沒有溢位,新連線請求的SYN也可能被drop

Accept backlog is full. If we have already queued enough of warm entries in SYN queue, drop request. It is better than clogging SYN queue with openreqs with exponentially increasing timeout.

clog:

如何檢視accept queue溢位

Netstat -s | grep LISTEN 返回***SYNS to LISTEN sockets ignored

以下案例全部來自維信公眾號基礎架構快報”TCP三次握手之backlog”,感興趣的可以加微信閱讀原文。

案例1

Nginx作為7層反向代理,客戶端HTTP請求– NGINX – 透明代理,透明代理介面存在大量慢請求;

思路

抓包,客戶端同nginx通訊,nginx立即返回ACK(1ms),但是3s後才返回響應資料;

Nginx同後端通訊,傳送SYN請求等待3s後端才響應;

結論

Backlog設定過小,導致accept queue溢位,SYN被丟棄導致3s重傳;

backlog50增加到512somaxconn=512

案例2

Testserver隨機生成RAR/ZIP檔案,testclient訪問testserver獲取生成檔案,所有呼叫採用block方式;

執行一段時間後程序永久阻塞,strace發現testclient阻塞在recvmsg

思路

抓包觀察3次握手協議,伺服器返回SYN+ACK,客戶端響應ACK,可伺服器再次傳送同樣的SYN+ACK

客戶端響應的ACK丟包,而net.ipv4.tcp_synack_retries = 1

結論

三次握手最後一步失敗,server保持SYN_RECV狀態等待接收ACKclient傳送ACK狀態變為ESTABLISHED其認為connect()成功故接著呼叫recvmsg()

Syn+ack被設定為只重傳一次,若這次重傳仍失敗,則客戶端永久阻塞

其他案例1

Backlog過大,連線積壓在accept queuenginx由於連線超時而斷開PHP accept返回時連線已被客戶端close,故報告PHP write Broken pipe

其他案例2

Backlog過小,accept queue溢位,握手第3步的ACK被丟棄,但client認為連線成功併發送資料,造成所謂慢請求


http://jaseywang.me/2014/07/20/tcp-queue-%E7%9A%84%E4%B8%80%E4%BA%9B%E9%97%AE%E9%A2%98/