您好,登錄后才能下訂單哦!
這篇文章主要講解了“如何理解TCP半連接隊列和全連接隊列”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“如何理解TCP半連接隊列和全連接隊列”吧!
JAVA的client和server,使用socket通信。server使用NIO。 1.間歇性的出現client向server建立連接三次握手已經完成,但server的selector沒有響應到這連接。 2.出問題的時間點,會同時有很多連接出現這個問題。 3.selector沒有銷毀重建,一直用的都是一個。 4.程序剛啟動的時候必會出現一些,之后會間歇性出現。
第一步:client 發送 syn 到server 發起握手;
第二步:server 收到 syn后回復syn+ack給client;
第三步:client 收到syn+ack后,回復server一個ack表示收到了server的syn+ack(此時client的56911端口的連接已經是established)
從問題的描述來看,有點像TCP建連接的時候全連接隊列(accept隊列)滿了,尤其是癥狀2、4. 為了證明是這個原因,馬上通過 ss -s 去看隊列的溢出統計數據:
667399 times the listen queue of a socket overflowed
反復看了幾次之后發現這個overflowed 一直在增加,那么可以明確的是server上全連接隊列一定溢出了
接著查看溢出后,OS怎么處理:
# cat /proc/sys/net/ipv4/tcp_abort_on_overflow 0
tcp_abort_on_overflow 為0表示如果三次握手第三步的時候全連接隊列滿了那么server扔掉client 發過來的ack(在server端認為連接還沒建立起來)
為了證明客戶端應用代碼的異常跟全連接隊列滿有關系,我先把tcp_abort_on_overflow修改成 1,1表示第三步的時候如果全連接隊列滿了,server發送一個reset包給client,表示廢掉這個握手過程和這個連接(本來在server端這個連接就還沒建立起來)。
接著測試然后在客戶端異常中可以看到很多connection reset by peer的錯誤,到此證明客戶端錯誤是這個原因導致的。
于是開發同學翻看java 源代碼發現socket 默認的backlog(這個值控制全連接隊列的大小,后面再詳述)是50,于是改大重新跑,經過12個小時以上的壓測,這個錯誤一次都沒出現過,同時 overflowed 也不再增加了。
到此問題解決,簡單來說TCP三次握手后有個accept隊列,進到這個隊列才能從Listen變成accept,默認backlog 值是50,很容易就滿了。滿了之后握手第三步的時候server就忽略了client發過來的ack包(隔一段時間server重發握手第二步的syn+ack包給client),如果這個連接一直排不上隊就異常了。
如上圖所示,這里有兩個隊列:syns queue(半連接隊列);accept queue(全連接隊列)
三次握手中,在第一步server收到client的syn后,把相關信息放到半連接隊列中,同時回復syn+ack給client(第二步);
比如syn floods 攻擊就是針對半連接隊列的,攻擊方不停地建連接,但是建連接的時候只做第一步,第二步中攻擊方收到server的syn+ack后故意扔掉什么也不做,導致server上這個隊列滿其它正常請求無法進來
第三步的時候server收到client的ack,如果這時全連接隊列沒滿,那么從半連接隊列拿出相關信息放入到全連接隊列中,否則按tcp_abort_on_overflow指示的執行。
這時如果全連接隊列滿了并且tcp_abort_on_overflow是0的話,server過一段時間再次發送syn+ack給client(也就是重新走握手的第二步),如果client超時等待比較短,就很容易異常了。
在我們的os中retry 第二步的默認次數是2(centos默認是5次):
net.ipv4.tcp_synack_retries = 2
上述解決過程有點繞,那么下次再出現類似問題有什么更快更明確的手段來確認這個問題呢?
[root@server ~]# netstat -s | egrep "listen|LISTEN">667399 times the listen queue of a socket overflowed 667399 SYNs to LISTEN sockets ignored
比如上面看到的 667399 times ,表示全連接隊列溢出的次數,隔幾秒鐘執行下,如果這個數字一直在增加的話肯定全連接隊列偶爾滿了。
[root@server ~]# ss -lnt Recv-Q Send-Q Local Address:Port Peer Address:Port 0 50 *:3306 *:*
上面看到的第二列Send-Q 表示第三列的listen端口上的全連接隊列最大為50,第一列Recv-Q為全連接隊列當前使用了多少
全連接隊列的大小取決于:min(backlog, somaxconn) . backlog是在socket創建的時候傳入的,somaxconn是一個os級別的系統參數
半連接隊列的大小取決于:max(64, /proc/sys/net/ipv4/tcp_max_syn_backlog)。 不同版本的os會有些差異
把java中backlog改成10(越小越容易溢出),繼續跑壓力,這個時候client又開始報異常了,然后在server上通過 ss 命令觀察到:
Fri May 5 13:50:23 CST 2017 Recv-Q Send-QLocal Address:Port Peer Address:Port 11 10 *:3306 *:*
按照前面的理解,這個時候我們能看到3306這個端口上的服務全連接隊列最大是10,但是現在有11個在隊列中和等待進隊列的,肯定有一個連接進不去隊列要overflow掉
如果client走完第三步在client看來連接已經建立好了,但是server上的對應連接實際沒有準備好,這個時候如果client發數據給server,server會怎么處理呢?(有同學說會reset,還是實踐看看)
先來看一個例子:
如上圖,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 (感謝 @劉歡(淺奕(16:00后答疑) 的提示)
**從上面的實際抓包來看不是reset,而是server忽略這些包,然后client重傳,一定次數后client認為異常,然后斷開連接。
**
[root@server ~]# date; netstat -s | egrep "listen|LISTEN">5 15:39:58 CST 2017 1641685 times the listen queue of a socket overflowed 1641685 SYNs to LISTEN sockets ignored [root@server ~]# date; netstat -s | egrep "listen|LISTEN" Fri May 5 15:39:59 CST 2017 1641906 times the listen queue of a socket overflowed 1641906 SYNs to LISTEN sockets ignored
如上所示:
overflowed和ignored居然總是一樣多,并且都是同步增加,overflowed表示全連接隊列溢出次數,socket ignored表示半連接隊列溢出次數,沒這么巧吧。
翻看內核源代碼:
可以看到overflow的時候一定會drop++(socket ignored),也就是drop一定大于等于overflow。
同時我也查看了另外幾臺server的這兩個值來證明drop一定大于等于overflow:
server1150 SYNs to LISTEN sockets dropped server2 193 SYNs to LISTEN sockets dropped server3 16329 times the listen queue of a socket overflowed 16422 SYNs to LISTEN sockets dropped server4 20 times the listen queue of a socket overflowed 51 SYNs to LISTEN sockets dropped server5 984932 times the listen queue of a socket overflowed 988003 SYNs to LISTEN sockets dropped
感謝各位的閱讀,以上就是“如何理解TCP半連接隊列和全連接隊列”的內容了,經過本文的學習后,相信大家對如何理解TCP半連接隊列和全連接隊列這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。