當(dāng)前位置:首頁(yè) > 芯聞號(hào) > 充電吧
[導(dǎo)讀]“三次握手,四次揮手”你確定你了解么?(之三)

連接隊(duì)列

在外部請(qǐng)求到達(dá)時(shí),被服務(wù)程序最終感知到前,連接可能處于SYN_RCVD狀態(tài)或是ESTABLISHED狀態(tài),但還未被應(yīng)用程序接受。

對(duì)應(yīng)地,服務(wù)器端也會(huì)維護(hù)兩種隊(duì)列,處于SYN_RCVD狀態(tài)的半連接隊(duì)列,而處于ESTABLISHED狀態(tài)但仍未被應(yīng)用程序accept的為全連接隊(duì)列。如果這兩個(gè)隊(duì)列滿了之后,就會(huì)出現(xiàn)各種丟包的情形。

1

2

查看是否有連接溢出

netstat -s | grep LISTEN


半連接隊(duì)列滿了

在三次握手協(xié)議中,服務(wù)器維護(hù)一個(gè)半連接隊(duì)列,該隊(duì)列為每個(gè)客戶端的SYN包開設(shè)一個(gè)條目(服務(wù)端在接收到SYN包的時(shí)候,就已經(jīng)創(chuàng)建了request_sock結(jié)構(gòu),存儲(chǔ)在半連接隊(duì)列中),該條目表明服務(wù)器已收到SYN包,并向客戶發(fā)出確認(rèn),正在等待客戶的確認(rèn)包。這些條目所標(biāo)識(shí)的連接在服務(wù)器處于Syn_RECV狀態(tài),當(dāng)服務(wù)器收到客戶的確認(rèn)包時(shí),刪除該條目,服務(wù)器進(jìn)入ESTABLISHED狀態(tài)。

目前,Linux下默認(rèn)會(huì)進(jìn)行5次重發(fā)SYN-ACK包,重試的間隔時(shí)間從1s開始,下次的重試間隔時(shí)間是前一次的雙倍,5次的重試時(shí)間間隔為1s, 2s, 4s, 8s, 16s, 總共31s, 稱為指數(shù)退避,第5次發(fā)出后還要等32s才知道第5次也超時(shí)了,所以,總共需要 1s + 2s + 4s+ 8s+ 16s + 32s = 63s, TCP才會(huì)把斷開這個(gè)連接。由于,SYN超時(shí)需要63秒,那么就給攻擊者一個(gè)攻擊服務(wù)器的機(jī)會(huì),攻擊者在短時(shí)間內(nèi)發(fā)送大量的SYN包給Server(俗稱SYN flood攻擊),用于耗盡Server的SYN隊(duì)列。對(duì)于應(yīng)對(duì)SYN 過(guò)多的問(wèn)題,linux提供了幾個(gè)TCP參數(shù):tcp_syncookies、tcp_synack_retries、tcp_max_syn_backlog、tcp_abort_on_overflow 來(lái)調(diào)整應(yīng)對(duì)。

參數(shù)作用
tcp_syncookiesSYNcookie將連接信息編碼在ISN(initialsequencenumber)中返回給客戶端,這時(shí)server不需要將半連接保存在隊(duì)列中,而是利用客戶端隨后發(fā)來(lái)的ACK帶回的ISN還原連接信息,以完成連接的建立,避免了半連接隊(duì)列被攻擊SYN包填滿。
tcp_syncookies內(nèi)核放棄建立連接之前發(fā)送SYN包的數(shù)量。
tcp_synack_retries內(nèi)核放棄連接之前發(fā)送SYN+ACK包的數(shù)量
tcp_max_syn_backlog默認(rèn)為1000. 這表示半連接隊(duì)列的長(zhǎng)度,如果超過(guò)則放棄當(dāng)前連接。
tcp_abort_on_overflow如果設(shè)置了此項(xiàng),則直接reset. 否則,不做任何操作,這樣當(dāng)服務(wù)器半連接隊(duì)列有空了之后,會(huì)重新接受連接。Linux堅(jiān)持在能力許可范圍內(nèi)不忽略進(jìn)入的連接??蛻舳嗽谶@期間會(huì)重復(fù)發(fā)送sys包,當(dāng)重試次數(shù)到達(dá)上限之后,會(huì)得到connection time out響應(yīng)。

全連接隊(duì)列滿了

當(dāng)?shù)谌挝帐謺r(shí),當(dāng)server接收到ACK包之后,會(huì)進(jìn)入一個(gè)新的叫 accept 的隊(duì)列。

當(dāng)accept隊(duì)列滿了之后,即使client繼續(xù)向server發(fā)送ACK的包,也會(huì)不被響應(yīng),此時(shí)ListenOverflows+1,同時(shí)server通過(guò)tcp_abort_on_overflow來(lái)決定如何返回,0表示直接丟棄該ACK,1表示發(fā)送RST通知client;相應(yīng)的,client則會(huì)分別返回read timeout?或者?connection reset by peer。另外,tcp_abort_on_overflow是0的話,server過(guò)一段時(shí)間再次發(fā)送syn+ack給client(也就是重新走握手的第二步),如果client超時(shí)等待比較短,就很容易異常了。而客戶端收到多個(gè) SYN ACK 包,則會(huì)認(rèn)為之前的 ACK 丟包了。于是促使客戶端再次發(fā)送 ACK ,在 accept隊(duì)列有空閑的時(shí)候最終完成連接。若 accept隊(duì)列始終滿員,則最終客戶端收到 RST 包(此時(shí)服務(wù)端發(fā)送syn+ack的次數(shù)超出了tcp_synack_retries)。

服務(wù)端僅僅只是創(chuàng)建一個(gè)定時(shí)器,以固定間隔重傳syn和ack到服務(wù)端

參數(shù)作用
tcp_abort_on_overflow如果設(shè)置了此項(xiàng),則直接reset. 否則,不做任何操作,這樣當(dāng)服務(wù)器半連接隊(duì)列有空了之后,會(huì)重新接受連接。Linux堅(jiān)持在能力許可范圍內(nèi)不忽略進(jìn)入的連接??蛻舳嗽谶@期間會(huì)重復(fù)發(fā)送sys包,當(dāng)重試次數(shù)到達(dá)上限之后,會(huì)得到connection time out響應(yīng)。
min(backlog, somaxconn)全連接隊(duì)列的長(zhǎng)度。

命令

netstat -s命令

1

2

3

[root<a href="http://www.jobbole.com/members/server">@server</a> ~]#??netstat -s | egrep "listen|LISTEN"

667399 times the listen queue of a socket overflowed

667399 SYNs to LISTEN sockets ignored

上面看到的 667399 times ,表示全連接隊(duì)列溢出的次數(shù),隔幾秒鐘執(zhí)行下,如果這個(gè)數(shù)字一直在增加的話肯定全連接隊(duì)列偶爾滿了。

1

[root<a href="http://www.jobbole.com/members/server">@server</a> ~]#??netstat -s | grep TCPBacklogDrop

查看 Accept queue 是否有溢出

ss命令

1

2

3

4

[root<a href="http://www.jobbole.com/members/server">@server</a> ~]#??ss -lnt

State Recv-Q Send-Q Local Address:Port Peer Address:Port

LISTEN???? 0??????128 *:6379 *:*

LISTEN???? 0??????128 *:22 *:*

如果State是listen狀態(tài),Send-Q 表示第三列的listen端口上的全連接隊(duì)列最大為50,第一列Recv-Q為全連接隊(duì)列當(dāng)前使用了多少。
非 LISTEN 狀態(tài)中 Recv-Q 表示 receive queue 中的 bytes 數(shù)量;Send-Q 表示 send queue 中的 bytes 數(shù)值。

小結(jié)

當(dāng)外部連接請(qǐng)求到來(lái)時(shí),TCP模塊會(huì)首先查看max_syn_backlog,如果處于SYN_RCVD狀態(tài)的連接數(shù)目超過(guò)這一閾值,進(jìn)入的連接會(huì)被拒絕。根據(jù)tcp_abort_on_overflow字段來(lái)決定是直接丟棄,還是直接reset.

從服務(wù)端來(lái)說(shuō),三次握手中,第一步server接受到client的syn后,把相關(guān)信息放到半連接隊(duì)列中,同時(shí)回復(fù)syn+ack給client. 第三步當(dāng)收到客戶端的ack, 將連接加入到全連接隊(duì)列。

一般,全連接隊(duì)列比較小,會(huì)先滿,此時(shí)半連接隊(duì)列還沒(méi)滿。如果這時(shí)收到syn報(bào)文,則會(huì)進(jìn)入半連接隊(duì)列,沒(méi)有問(wèn)題。但是如果收到了三次握手中的第3步(ACK),則會(huì)根據(jù)tcp_abort_on_overflow字段來(lái)決定是直接丟棄,還是直接reset.此時(shí),客戶端發(fā)送了ACK, 那么客戶端認(rèn)為三次握手完成,它認(rèn)為服務(wù)端已經(jīng)準(zhǔn)備好了接收數(shù)據(jù)的準(zhǔn)備。但此時(shí)服務(wù)端可能因?yàn)槿B接隊(duì)列滿了而無(wú)法將連接放入,會(huì)重新發(fā)送第2步的syn+ack, 如果這時(shí)有數(shù)據(jù)到來(lái),服務(wù)器TCP模塊會(huì)將數(shù)據(jù)存入隊(duì)列中。一段時(shí)間后,client端沒(méi)收到回復(fù),超時(shí),連接異常,client會(huì)主動(dòng)關(guān)閉連接。

“三次握手,四次揮手”redis實(shí)例分析

  1. 我在dev機(jī)器上部署redis服務(wù),端口號(hào)為6379,

  2. 通過(guò)tcpdump工具獲取數(shù)據(jù)包,使用如下命令


1

2

tcpdump -w /tmp/a.cap port 6379 -s0

-w把數(shù)據(jù)寫入文件,-s0設(shè)置每個(gè)數(shù)據(jù)包的大小默認(rèn)為68字節(jié),如果用-S 0則會(huì)抓到完整數(shù)據(jù)包


  1. 在dev2機(jī)器上用redis-cli訪問(wèn)dev:6379, 發(fā)送一個(gè)ping, 得到回復(fù)pong

  2. 停止抓包,用tcpdump讀取捕獲到的數(shù)據(jù)包


1

2

tcpdump -r /tmp/a.cap -n -nn -A -x| vim -

-x 16進(jìn)制形式展示,便于后面分析)

共收到了7個(gè)包。

抓到的是IP數(shù)據(jù)包,IP數(shù)據(jù)包分為IP頭部和IP數(shù)據(jù)部分,IP數(shù)據(jù)部分是TCP頭部加TCP數(shù)據(jù)部分。

IP的數(shù)據(jù)格式為:

它由固定長(zhǎng)度20B+可變長(zhǎng)度構(gòu)成。

1

2

3

4

5

10:55:45.662077 IP dev2.39070 > dev.6379: Flags [S], seq 4133153791, win 29200, options [mss 1460,sackOK,TS val 2959270704 ecr 0,nop,wscale 7], length 0

????????0x0000:??4500 003c 08cf 4000 3606 14a5 0ab3 b561

????????0x0010:??0a60 5cd4 989e 18eb f65a ebff 0000 0000

????????0x0020:??a002 7210 872f 0000 0204 05b4 0402 080a

????????0x0030:??b062 e330 0000 0000 0103 0307

對(duì)著IP頭部格式,來(lái)拆解數(shù)據(jù)包的具體含義。

字節(jié)值字節(jié)含義
0x4IP版本為ipv4
0x5首部長(zhǎng)度為5 * 4字節(jié)=20B
0x00服務(wù)類型,現(xiàn)在基本都置為0
0x003c總長(zhǎng)度為3*16+12=60字節(jié),上面所有的長(zhǎng)度就是60字節(jié)
0x08cf標(biāo)識(shí)。同一個(gè)數(shù)據(jù)報(bào)的唯一標(biāo)識(shí)。當(dāng)IP數(shù)據(jù)報(bào)被拆分時(shí),會(huì)復(fù)制到每一個(gè)數(shù)據(jù)中。
0x40003bit 標(biāo)志 + 13bit 片偏移。3bit 標(biāo)志對(duì)應(yīng) R、DF、MF。目前只有后兩位有效,DF位:為1表示不分片,為0表示分片。MF:為1表示“更多的片”,為0表示這是最后一片。13bit 片位移:本分片在原先數(shù)據(jù)報(bào)文中相對(duì)首位的偏移位。(需要再乘以8 )
0x36生存時(shí)間TTL。IP報(bào)文所允許通過(guò)的路由器的最大數(shù)量。每經(jīng)過(guò)一個(gè)路由器,TTL減1,當(dāng)為 0 時(shí),路由器將該數(shù)據(jù)報(bào)丟棄。TTL 字段是由發(fā)送端初始設(shè)置一個(gè) 8 bit字段.推薦的初始值由分配數(shù)字 RFC 指定。發(fā)送 ICMP 回顯應(yīng)答時(shí)經(jīng)常把 TTL 設(shè)為最大值 255。TTL可以防止數(shù)據(jù)報(bào)陷入路由循環(huán)。 此處為54.
0x06協(xié)議類型。指出IP報(bào)文攜帶的數(shù)據(jù)使用的是哪種協(xié)議,以便目的主機(jī)的IP層能知道要將數(shù)據(jù)報(bào)上交到哪個(gè)進(jìn)程。TCP 的協(xié)議號(hào)為6,UDP 的協(xié)議號(hào)為17。ICMP 的協(xié)議號(hào)為1,IGMP 的協(xié)議號(hào)為2。該 IP 報(bào)文攜帶的數(shù)據(jù)使用 TCP 協(xié)議,得到了驗(yàn)證。
0x14a516bitIP首部校驗(yàn)和。
0x0ab3 b56132bit源ip地址。
0x0a60 5cd432bit目的ip地址。

剩余的數(shù)據(jù)部分即為TCP協(xié)議相關(guān)的。TCP也是20B固定長(zhǎng)度+可變長(zhǎng)度部分。

字節(jié)值字節(jié)含義
0x989e16bit源端口。1161616+81616+1416+11=39070
0x18eb16bit目的端口6379
0xf65a ebff32bit序列號(hào)。4133153791
0x0000 000032bit確認(rèn)號(hào)。
0xa4bit首部長(zhǎng)度,以4byte為單位。共10*4=40字節(jié)。因此TCP報(bào)文的可選長(zhǎng)度為40-20=20
0b0000006bit保留位。目前置為0.
0b0000106bitTCP標(biāo)志位。從左到右依次是緊急 URG、確認(rèn) ACK、推送 PSH、復(fù)位 RST、同步 SYN 、終止 FIN。
0x7210滑動(dòng)窗口大小,滑動(dòng)窗口即tcp接收緩沖區(qū)的大小,用于tcp擁塞控制。29200
0x872f16bit校驗(yàn)和。
0x0000緊急指針。僅在 URG = 1時(shí)才有意義,它指出本報(bào)文段中的緊急數(shù)據(jù)的字節(jié)數(shù)。當(dāng) URG = 1 時(shí),發(fā)送方 TCP 就把緊急數(shù)據(jù)插入到本報(bào)文段數(shù)據(jù)的最前面,而在緊急數(shù)據(jù)后面的數(shù)據(jù)仍是普通數(shù)據(jù)。

可變長(zhǎng)度部分,協(xié)議如下:

字節(jié)值字節(jié)含義
0x0204 05b4最大報(bào)文長(zhǎng)度為,05b4=1460. 即可接收的最大包長(zhǎng)度,通常為MTU減40字節(jié),IP頭和TCP頭各20字節(jié)
0x0402表示支持SACK
0x080a b062 e330 0000 0000時(shí)間戳。Ts val=b062 e330=2959270704, ecr=0
0x01無(wú)操作
0x03 0307窗口擴(kuò)大因子為7. 移位7, 乘以128

這樣第一個(gè)包分析完了。dev2向dev發(fā)送SYN請(qǐng)求。也就是三次握手中的第一次了。
SYN seq(c)=4133153791

第二個(gè)包,dev響應(yīng)連接,ack=4133153792. 表明dev下次準(zhǔn)備接收這個(gè)序號(hào)的包,用于tcp字節(jié)注的順序控制。dev(也就是server端)的初始序號(hào)為seq=4264776963, syn=1.
SYN ack=seq(c)+1 seq(s)=4264776963

第三個(gè)包,client包確認(rèn),這里使用了相對(duì)值應(yīng)答。seq=4133153792, 等于第二個(gè)包的ack. ack=4264776964.
ack=seq(s)+1, seq=seq(c)+1
至此,三次握手完成。接下來(lái)就是發(fā)送ping和pong的數(shù)據(jù)了。

接著第四個(gè)包。

1

2

3

4

5

6

10:55:48.090073 IP dev2.39070 > dev.6379: Flags [P.], seq 1:15, ack 1, win 229, options [nop,nop,TS val 2959273132 ecr 3132256230], length 14

????????0x0000:??4500 0042 08d1 4000 3606 149d 0ab3 b561

????????0x0010:??0a60 5cd4 989e 18eb f65a ec00 fe33 5504

????????0x0020:??8018 00e5 4b5f 0000 0101 080a b062 ecac

????????0x0030:??bab2 6fe6 2a31 0d0a 2434 0d0a 7069 6e67

????????0x0040:??0d0a

tcp首部長(zhǎng)度為32B, 可選長(zhǎng)度為12B. IP報(bào)文的總長(zhǎng)度為66B, 首部長(zhǎng)度為20B, 因此TCP數(shù)據(jù)部分長(zhǎng)度為14B. seq=0xf65a ec00=4133153792
ACK, PSH. 數(shù)據(jù)部分為2a31 0d0a 2434 0d0a 7069 6e67 0d0a

1

2

3

4

5

6

0x2a31???????? -> *1

0x0d0a???????? -> rn

0x2434???????? -> $4

0x0d0a???????? -> rn

0x7069 0x6e67??-> ping

0x0d0a???????? -> rn

dev2向dev發(fā)送了ping數(shù)據(jù),第四個(gè)包完畢。

第五個(gè)包,dev2向dev發(fā)送ack響應(yīng)。
序列號(hào)為0xfe33 5504=4264776964, ack確認(rèn)號(hào)為0xf65a ec0e=4133153806=(4133153792+14).

第六個(gè)包,dev向dev2響應(yīng)pong消息。序列號(hào)fe33 5504,確認(rèn)號(hào)f65a ec0e, TCP頭部可選長(zhǎng)度為12B, IP數(shù)據(jù)報(bào)總長(zhǎng)度為59B, 首部長(zhǎng)度為20B, 因此TCP數(shù)據(jù)長(zhǎng)度為7B.
數(shù)據(jù)部分2b50 4f4e 470d 0a, 翻譯過(guò)來(lái)就是+PONGrn.

至此,Redis客戶端和Server端的三次握手過(guò)程分析完畢。

總結(jié)

“三次握手,四次揮手”看似簡(jiǎn)單,但是深究進(jìn)去,還是可以延伸出很多知識(shí)點(diǎn)的。比如半連接隊(duì)列、全連接隊(duì)列等等。以前關(guān)于TCP建立連接、關(guān)閉連接的過(guò)程很容易就會(huì)忘記,可能是因?yàn)橹皇撬烙浻脖沉藥讉€(gè)過(guò)程,沒(méi)有深入研究背后的原理。

所以,“三次握手,四次揮手”你真的懂了嗎?

參考資料

【redis】https://segmentfault.com/a/1190000015044878
【tcp option】https://blog.csdn.net/wdscq1234/article/details/52423272
【滑動(dòng)窗口】https://www.zhihu.com/question/32255109
【全連接隊(duì)列】http://jm.taobao.org/2017/05/25/525-1/
【client fooling】 https://github.com/torvalds/linux/commit/5ea8ea2cb7f1d0db15762c9b0bb9e7330425a071
【backlog RECV_Q】http://blog.51cto.com/59090939/1947443
【定時(shí)器】https://www.cnblogs.com/menghuanbiao/p/5212131.html
【隊(duì)列圖示】https://www.itcodemonkey.com/article/5834.html
【tcp flood攻擊】https://www.cnblogs.com/hubavyn/p/4477883.html
【MSS MTU】https://blog.csdn.net/LoseInVain/article/details/53694265


本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

倫敦2024年8月29日 /美通社/ -- 英國(guó)汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時(shí)1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動(dòng) BSP

北京2024年8月28日 /美通社/ -- 越來(lái)越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時(shí)企業(yè)卻面臨越來(lái)越多業(yè)務(wù)中斷的風(fēng)險(xiǎn),如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報(bào)道,騰訊和網(wǎng)易近期正在縮減他們對(duì)日本游戲市場(chǎng)的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)開幕式在貴陽(yáng)舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語(yǔ)權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對(duì)環(huán)境變化,經(jīng)營(yíng)業(yè)績(jī)穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤(rùn)率延續(xù)升勢(shì) 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長(zhǎng) 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競(jìng)爭(zhēng)力 堅(jiān)持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競(jìng)爭(zhēng)優(yōu)勢(shì)...

關(guān)鍵字: 通信 BSP 電信運(yùn)營(yíng)商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺(tái)與中國(guó)電影電視技術(shù)學(xué)會(huì)聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會(huì)上宣布正式成立。 活動(dòng)現(xiàn)場(chǎng) NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長(zhǎng)三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會(huì)上,軟通動(dòng)力信息技術(shù)(集團(tuán))股份有限公司(以下簡(jiǎn)稱"軟通動(dòng)力")與長(zhǎng)三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉