說起TCP的三次握手,大多數小夥伴多少都聽說過一些,是以本文不再贅述三次握手的詳細流程,而是重點關注三次握手中半連接配接隊列和全連接配接隊列流程,以及二者隊列滿了時的處理機制,最後分析下常見的三次握手的問題,這些問題大都也是和半連接配接隊列和全連接配接隊列相關的。
三次握手
TCP三向交握大緻流程如下:
- client 發送 syn 到server 發起握手;
- server 收到 syn後回複syn+ack給client;
- client 收到syn+ack後,回複server一個ack表示收到了server的syn+ack,這時表示連接配接建立完成。

三次握手對應抓包如下:
半連接配接隊列/全連接配接隊列
上面說的TCP三向交握并沒有提到半連接配接隊列和全連接配接隊列,其實三次握手流程中還涉及到半連接配接和全連接配接隊列之間的流轉動作,對應的流程如下:
- client 發送 syn 到server 發起握手;
- server 收到 syn後回複syn+ack給client,同時server端會将連接配接放到半連接配接隊列;
- client 收到syn+ack後,回複server一個ack表示收到了server的syn+ack,這時表示連接配接建立完成,此時server端會将連接配接放到全連接配接隊列(這時連接配接已經建立OK了,隻不過程序還無感覺,程序需要主動調用accept之後拿到該連接配接資訊,并配置設定對應fd之後就可以進行IO讀寫操作了)。
如上圖所示,這裡有兩個隊列:syns queue(半連接配接隊列);accept queue(全連接配接隊列)。
隊列滿時處理機制
半連接配接隊列和全連接配接隊列滿時有以下3種場景:
- 半連接配接滿了,全連接配接未滿:當半連接配接滿了時,預設處理機制是,TCP忽略請求,也不發送RST,為什麼要這麼處理呢?因為這樣做是暫時的,用戶端将重新發送SYN,期望不久就能得到服務。假如服務端響應一個RST,用戶端的connect就會傳回錯誤,而不是讓重傳機制來處理,這樣客戶無法區分SYN的RST是因為"該端口沒有在監聽"還是"該端口在監聽,隻不過它的隊列滿了"。
- 半連接配接未滿,全連接配接滿了:當全連接配接隊列滿了時,再往全連接配接隊列塞時作業系統會按照
的訓示執行,預設為0表示扔掉client發過來的ack(在server端認為連接配接還沒建立起來),為1表示發送一個reset包給client。tcp_abort_on_overflow
- 半連接配接和全連接配接都滿了:相當于集中了以上所述的2種場景的最差場景,新連接配接預設不處理讓TCP重傳機制進行重試,往全連接配接隊列塞時預設按照
訓示來執行。tcp_abort_on_overflow
半連接配接/全連接配接隊列的大小到底是多少呢?
準确來說,全連接配接隊列的大小取決于:min(backlog, somaxconn) . backlog是在socket建立的時候傳入的,somaxconn是一個os級别的系統參數,也就是/proc/sys/net/core/somaxconn的大小。半連接配接隊列的大小取決于:max(64, /proc/sys/net/ipv4/tcp_max_syn_backlog)。不同版本的os會有些差異。
常見問題分析
- 網站服務中常見的syn floods攻擊,就是針對半連接配接隊列攻擊的,攻擊用戶端不停地建立連接配接,但是它隻、不會處理接收到的第二步的響應結果,導緻server端半連接配接隊列滿了而無法處理正常進來的建立連接配接請求。
- 壓測場景中可能存在的連接配接建立不上問題,可能是預設的backlog較小導緻,這樣很容易導緻隊列滿了,然後忽略該請求封包,用戶端遲遲建立不上連接配接。
- 全連接配接隊列攻擊,攻擊用戶端隻連接配接而不進行發送資料的一種攻擊方式,如果服務端使用的是一個線程一個連接配接的處理方式,會白白占用很多線程資源;如果是NIO處理方式,雖然不用多占用線程資源,但是也是會白白占用fd資源的。
最後我們想一下,TCP為什麼需要3次握手呢?
一句話總結就是:3次握手保證了連接配接的可靠性與效率。3次握手過程中,會交換各自的一些資訊,比如視窗大小、初始封包序号等,如果隻有2次握手,不能保證用戶端一定能收到服務端的響應封包(SYN+ACK)。