TCP三次握手和四次揮手深入實(shí)踐

TCP連接狀態(tài)

圖1是TCP三次握手、數(shù)據(jù)傳輸、四次揮手三個(gè)階段的狀態(tài)轉(zhuǎn)移圖,狀態(tài)說(shuō)明如下:

  • LISTEN:偵聽(tīng)來(lái)自客戶端的TCP端口的連接請(qǐng)求
  • SYN-SENT:再發(fā)送連接請(qǐng)求后等待匹配的連接請(qǐng)求(如果有大量這樣的狀態(tài)包,檢查是否中招了)
  • SYN-RCVD:再收到和發(fā)送一個(gè)連接請(qǐng)求后等待對(duì)方對(duì)連接請(qǐng)求的確認(rèn)(如有大量此狀態(tài),估計(jì)被flood攻擊了)
  • ESTABLISHED:代表一個(gè)打開(kāi)的連接
  • FIN-WAIT-1:等待遠(yuǎn)程TCP連接中斷請(qǐng)求,或先前的連接中斷請(qǐng)求的確認(rèn)
  • FIN-WAIT-2:從遠(yuǎn)程TCP等待連接中斷請(qǐng)求
  • CLOSE-WAIT:等待從本地用戶發(fā)來(lái)的連接中斷請(qǐng)求
  • LAST-ACK:等待原來(lái)的發(fā)向遠(yuǎn)程TCP的連接中斷請(qǐng)求的確認(rèn)(不是什么好東西,此項(xiàng)出現(xiàn),檢查是否被攻擊)
  • TIME-WAIT:等待足夠的時(shí)間以確保遠(yuǎn)程TCP接收到連接中斷請(qǐng)求的確認(rèn)
  • CLOSED:沒(méi)有任何連接狀態(tài),連接結(jié)束

本文通過(guò)實(shí)踐例子模擬每個(gè)階段的狀態(tài)變化。

圖1 TCP連接狀態(tài)轉(zhuǎn)移圖

本文用tcpdump抓包分析TCP連接的交互過(guò)程,其中tcpdump Flags含義如下:

  • S=SYN 發(fā)起連接標(biāo)志
  • P=PUSH 傳送數(shù)據(jù)標(biāo)志
  • F=FIN 關(guān)閉連接標(biāo)志
  • R=RESET 異常關(guān)閉連接,鏈接重置
  • . 表示沒(méi)有任何標(biāo)志,表示返回ack

為什么要三次握手?

  • 如果只有一次握手,Client不能確定與Server的單向連接,更加不能確定Server與Client的單向連接;
  • 如果只有兩次握手,Client確定與Server的單向連接,但是Server不能確定與Client的單向連接;
  • 只有三次握手,Client與Server才能相互確認(rèn)雙向連接,實(shí)現(xiàn)雙工數(shù)據(jù)傳輸。
圖2 TCP三次握手

為什么要四次揮手?

“三次握手”的第二次握手發(fā)送SYN+ACK回應(yīng)第一次握手的SYN,但是“四次揮手”的第二次揮手只能發(fā)送ACK回應(yīng)第一次揮手的FIN,因?yàn)榇藭r(shí)Server可能還有數(shù)據(jù)傳輸給Client,所以Server傳輸數(shù)據(jù)完成后才能發(fā)起第三次揮手發(fā)送FIN給Client,等待Client的第四次揮手ACK。

圖3 TCP四次揮手

下面通過(guò)例子模擬TCP連接過(guò)程的狀態(tài)轉(zhuǎn)移。

情況1:Client啟動(dòng)服務(wù),Server不啟動(dòng)服務(wù)

Server(127.0.0.1:2017)未啟動(dòng)服務(wù),如圖4抓包所示,Client(127.0.0.1:32906)發(fā)送SYN(localhost.32906 > localhost.2017: Flags [S])啟動(dòng)TCP連接,Server返回localhost.2017 > localhost.32906: Flags [R.],R=RESET表示異常關(guān)閉連接,連接重置。

圖4 Client啟動(dòng)服務(wù),Server不啟動(dòng)服務(wù)(tcpdump)
情況2:Server啟動(dòng)服務(wù),Client不啟動(dòng)服務(wù)

如圖5所示,Server(127.0.0.1:2017)監(jiān)聽(tīng)2017端口,TCP連接狀態(tài)是LISTEN,等待Client發(fā)起TCP連接,如圖6所示,tcpdump抓包沒(méi)有連接數(shù)據(jù)。

圖5 Server啟動(dòng)服務(wù),Client不啟動(dòng)服務(wù)(netstat))
圖6 Server啟動(dòng)服務(wù),Client不啟動(dòng)服務(wù)(tcpdump)
情況3:Client連接Server

如圖7所示,Server(127.0.0.1:2017)啟動(dòng)2017端口監(jiān)聽(tīng)連接,Client(127.0.0.1:55702)啟動(dòng)55702端口訪問(wèn)2017端口的Server,由圖7、圖1和圖2比對(duì),兩端的雙向連接已經(jīng)建立,TCP狀態(tài)轉(zhuǎn)移到ESTABLISHED。正常情況下,SYN-SENT和SYN-RCVD轉(zhuǎn)移非常快,netstat難以捕獲,捕獲場(chǎng)景請(qǐng)查看下面異常情況1和異常情況2。
如圖8抓包所示,三行數(shù)據(jù)對(duì)應(yīng)圖2的三次握手,說(shuō)明雙向連接已經(jīng)建立。

圖7 Client連接Server(netstat)
圖8 Client連接Server(tcpdump)
情況4:Client傳輸數(shù)據(jù)

從圖1和圖9看,數(shù)據(jù)傳輸過(guò)程中Server(127.0.0.1:2017)和Client(127.0.0.1:55866)的TCP連接狀態(tài)均為ESTABLISHED。

圖9 Client數(shù)據(jù)傳輸(netstat)

從圖10紅色方框看,Client(localhost:55866)向Server(localhost:2017)發(fā)送數(shù)據(jù)(localhost.55866 > localhost.2017: Flags [P.]),Server返回ack(localhost.2017 > localhost.55866: Flags [.])確認(rèn)。

圖10 Client數(shù)據(jù)傳輸抓包(tcpdump)
情況5:Client斷開(kāi)連接,Server保持連接

Client(localhost.41416)主動(dòng)斷掉TCP連接,進(jìn)入ESTABLISHED -> FIN-WAIT-1 -> FIN-WAIT-2 -> TIME-WAIT狀態(tài),分別順序?qū)?yīng)圖11紅色方框的三行記錄,對(duì)比圖3,第一行表示第一次揮手,第二行表示第二/三次揮手,第三行表示第四次揮手。

圖11 Client斷開(kāi)連接,Server保持服務(wù)(tcpdump)

對(duì)比12和圖3,Client(127.0.0.1:41416)主動(dòng)斷掉TCP連接,進(jìn)入ESTABLISHED -> FIN-WAIT-1 -> FIN-WAIT-2 -> TIME-WAIT狀態(tài),等待2MSL,如果2MSL內(nèi)Server(127.0.0.1:2017)沒(méi)有發(fā)送FIN,意味著Server已經(jīng)接收到Client的FIN ACK,1分鐘(/proc/sys/net/ipv4/tcp_fin_timeout:60)超時(shí)后Client TCP狀態(tài)轉(zhuǎn)移為CLOSED,符合TCP四次揮手邏輯。

圖12 Client斷開(kāi)連接,Server保持服務(wù)(netstat)
情況6:Server斷開(kāi)連接,Client保持連接

Server(localhost:2017)主動(dòng)斷掉TCP連接,根據(jù)圖13倒數(shù)第二行,對(duì)比圖3(Server/Client反著對(duì)比),Server發(fā)送FIN后TCP狀態(tài)由ESTABLISHED轉(zhuǎn)移到FIN-WAIT-1,根據(jù)圖13倒數(shù)第一行,Server接收到Client(localhost:40277)發(fā)送的FIN ACK,由FIN-WAIT-1轉(zhuǎn)移到FIN-WAIT-2狀態(tài),如圖14所示,超時(shí)后轉(zhuǎn)移到CLOSED。
如圖14所示,Client(127.0.0.1:40277)回應(yīng)Server FIN ACK,但是Client未斷開(kāi)連接,TCP狀態(tài)由ESTABLISHED轉(zhuǎn)移為CLOSE_WAIT,不會(huì)由CLOSE_WAIT轉(zhuǎn)移為L(zhǎng)AST-ACK。
如圖15所示,Client(localhost:40277)關(guān)閉連接,發(fā)送FIN,Client由CLOSE_WAIT -> LAST-ACK -> CLOSED。Server(localhost:2017)已經(jīng)關(guān)閉,故回復(fù)localhost.2017 > localhost.40277: Flags [R],R=RESET表示異常關(guān)閉連接,連接重置。

圖13 Server斷開(kāi)連接,Client保持連接(tcpdump)
圖14 Server斷開(kāi)連接,Client保持連接(netstat)
圖15 Client關(guān)閉連接(tcpdump)

正常情況下,SYN-SENT、SYN-RCVD、LAST-ACK這些狀態(tài)轉(zhuǎn)移非???,netstat很難查看到,如果netstat出現(xiàn)這些狀態(tài),有可能受到攻擊,下面將模擬這些場(chǎng)景。

異常情況1:模擬出現(xiàn)SYN-SENT

從圖2看到,正常情況,三次握手Client的TCP狀態(tài)很快轉(zhuǎn)移到ESTABLISHED,不會(huì)長(zhǎng)時(shí)間停留在SYN_SENT。但是如果Server不返回SYN ACK,Client通過(guò)netstat可以查看到SYN_SENT。
1.設(shè)置防火墻iptables丟棄發(fā)送給Server(127.0.0.1:2017)的數(shù)據(jù)包,這樣Server不會(huì)響應(yīng)Client發(fā)送的SYN而返回SYN ACK。如圖16,把紅色打叉的傳輸過(guò)程掐斷,防火墻丟棄Client發(fā)送給Server的SYN。

iptables -I INPUT -s 127.0.0.1 -p tcp --dport 2017 -j DROP
圖16 設(shè)置防火墻丟棄Client發(fā)送的SYN

2.Client(localhost:35996)發(fā)送SYN給Server(localhost:2017),如圖17所示,從localhost.35996 > localhost.2017: Flags [S]行記錄看,Client TCP連接狀態(tài)轉(zhuǎn)移到SYN_SENT,防火墻丟棄發(fā)送給Server的SYN,Server端也就不會(huì)發(fā)送SYN ACK給Client,結(jié)合圖16和圖18,Client TCP連接狀態(tài)不會(huì)由SYN_SENT轉(zhuǎn)移到ESTABLISHED,Server TCP連接狀態(tài)不會(huì)由LISTEN轉(zhuǎn)移到SYN_RCVD。
如圖17紅色方框所示,Client端以2的倍數(shù)遞增的間隔重試6次,重試時(shí)間間隔:1s、2s、4s、8s、16s、32s。如圖18、19所示,Client TCP連接狀態(tài)保持在SYN_SENT,超時(shí)后轉(zhuǎn)移到CLOSED,等待時(shí)長(zhǎng)對(duì)應(yīng)圖17的重試時(shí)間。

圖17 模擬出現(xiàn)SYN-SENT(tcpdump)
圖18 Client端TCP連接狀態(tài)SYN_SENT等待時(shí)長(zhǎng)1
圖19 Client端TCP連接狀態(tài)SYN_SENT等待時(shí)長(zhǎng)2

3.客戶端握手超時(shí)報(bào)錯(cuò)信息:Connection timed out

Exception in thread "main" java.net.ConnectException: Connection timed out (Connection timed out)
    at java.net.PlainSocketImpl.socketConnect(Native Method)
    at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
    at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
    at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
    at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
    at java.net.Socket.connect(Socket.java:589)
    at java.net.Socket.connect(Socket.java:538)
    at java.net.Socket.<init>(Socket.java:434)
    at java.net.Socket.<init>(Socket.java:211)
    at io.socket.Client1.main(Client1.java:17)
異常情況2:模擬出現(xiàn)SYN-RCVD

從圖2看到,正常情況,Server的TCP連接狀態(tài)很快轉(zhuǎn)移到ESTABLISHED,不會(huì)長(zhǎng)時(shí)間停留在SYN_RCVD。但是如果Client不返回SYN ACK,Server通過(guò)netstat可以查看到SYN_RCVD。
1.設(shè)置防火墻丟棄Server(127.0.0.1:2017)發(fā)送出去的數(shù)據(jù)包,這樣Client不會(huì)響應(yīng)Server發(fā)送的SYN而返回SYN ACK。如圖20,把紅色打叉的傳輸過(guò)程掐斷,防火墻丟棄Server發(fā)送的SYN&ACK。

iptables -I INPUT -s 127.0.0.1 -p tcp --sport 2017 -j DROP
圖20 設(shè)置防火墻丟棄Server發(fā)送的SYN&ACK

2.Client(localhost:36436)發(fā)送SYN給Server(localhost:2017),Server發(fā)送SYN ACK給Client,但是被防火墻丟棄Server發(fā)送Client端的SYN&ACK,Client也就不會(huì)發(fā)送SYN ACK給Server,結(jié)合圖20和圖22,Client TCP連接狀態(tài)轉(zhuǎn)移為SYN_SENT,但是不會(huì)轉(zhuǎn)移到ESTABLISHED,Server TCP連接狀態(tài)由LISTEN轉(zhuǎn)移到SYN_RCVD,但是不會(huì)轉(zhuǎn)移到ESTABLISHED。
如圖21紅色方框所示,Client收不到Server 的SYN ACK,從localhost.36436 > localhost.2017: Flags [S.]的行記錄看,Client以2的倍數(shù)遞增的間隔重試6次,重試時(shí)間間隔:1s、2s、4s、8s、16s、32s。
Server收不到Client發(fā)送的SYN ACK,從localhost.2017 > localhost.36436: Flags [S.]的行記錄看,Server端以2的倍數(shù)遞增的間隔重試5次,重試時(shí)間間隔:1s、2s、4s、8s、16s。
如圖22所示,Client TCP連接狀態(tài)保持在SYN_SENT,超時(shí)后轉(zhuǎn)移到CLOSED,等待時(shí)長(zhǎng)對(duì)應(yīng)圖21的重試時(shí)間。Server TCP連接狀態(tài)保持在SYN_RCVD,超時(shí)后轉(zhuǎn)移到CLOSED,等待時(shí)長(zhǎng)對(duì)應(yīng)圖21的重試時(shí)間。

圖21 模擬出現(xiàn)SYN-RCVD(tcpdump)
圖22 TCP連接狀態(tài)SYN_SENT/SYN_RCVD等待時(shí)長(zhǎng)
異常情況3:模擬出現(xiàn)FIN_WAIT1

從圖3看到,正常情況,四次握手Client斷開(kāi)連接后,TCP連接狀態(tài)很快轉(zhuǎn)移到FIN_WAIT2,不會(huì)長(zhǎng)時(shí)間停留在FIN_WAIT1。但是如果Server不返回FIN ACK,Client通過(guò)netstat可以查看到FIN_WAIT1。
1.Client在斷開(kāi)連接之前,設(shè)置防火墻丟棄Client發(fā)送給Server(127.0.0.1:2017)的數(shù)據(jù)包,這樣Server不會(huì)響應(yīng)Client發(fā)送的FIN而返回FIN ACK。如圖23,把紅色打叉的傳輸過(guò)程掐斷,防火墻丟棄Client發(fā)送的FIN。

 iptables -I INPUT -s 127.0.0.1 -p tcp --dport 2017 -j DROP
圖23 設(shè)置防火墻丟棄Client發(fā)送的FIN

2.設(shè)置防火墻丟棄Client(127.0.0.1:40604)發(fā)送出去的包,Client主動(dòng)斷開(kāi)連接,此時(shí)Server保持連接,如圖24所示, 從localhost.40604 -> localhost.2017 Flags [F.]的行記錄,Client發(fā)送FIN給Server,但是被防火墻丟棄,結(jié)合圖23和圖25,Client的TCP狀態(tài)轉(zhuǎn)移過(guò)程:ESTABLISHED -> FIN_WAIT1,Client收不到Server發(fā)送的FIN ACK, TCP狀態(tài)不能由FIN_WAIT1轉(zhuǎn)移到FIN_WAIT2;Server TCP狀態(tài)為ESTABLISHED,Server接收不到Client的FIN而沒(méi)有發(fā)送FIN ACK, TCP狀態(tài)不能由ESTABLISHED轉(zhuǎn)移到CLOSE_WAIT。
如圖24紅色方框所示,Client收不到Server發(fā)送的FIN ACK,從localhost.40604 -> localhost.2017 Flags [F.]行記錄看,Client重試6次,重試時(shí)間間隔:1s、1s、2s、3s、6s、14s、26s。
如圖25所示,Client TCP連接狀態(tài)保持在FIN_WAIT1,超時(shí)后轉(zhuǎn)移到CLOSED,等待時(shí)長(zhǎng)對(duì)應(yīng)圖24的重試時(shí)間。

圖24 模擬出現(xiàn)FIN_WAIT1(tcpdump)
圖25 TCP連接狀態(tài)FIN_WAIT1等待時(shí)長(zhǎng)
異常情況4:模擬出現(xiàn)LAST-ACK

從圖3看,Server主動(dòng)斷開(kāi)連接,實(shí)際上此時(shí)Server變成客戶端,Client和Server的TCP狀態(tài)反著查看TCP四次揮手,Client發(fā)送FIN&ACK后CLOSE-WAIT -> LAST-ACK,接收到Server的FIN ACK后LAST-ACK -> CLOSED。
1.Server(127.0.0.1:2017)主動(dòng)斷開(kāi)連接后,Client(127.0.0.1:40541)主動(dòng)斷開(kāi)連接前,設(shè)置防火墻丟棄Client發(fā)送出去的包,這樣Server不能接收到Client發(fā)送的FIN,Client(127.0.0.1:40541)TCP狀態(tài)由CLOSE-WAIT過(guò)渡到LAST-ACK,超時(shí)由LAST-ACK轉(zhuǎn)移為CLOSED。如圖26,把紅色打叉的傳輸過(guò)程掐斷,防火墻丟棄Client FIN。

iptables -I INPUT -s 127.0.0.1 -p tcp --sport 40541 -j DROP
圖26 設(shè)置防火墻丟棄Client FIN

2.Server(127.0.0.1:2017)主動(dòng)斷開(kāi)連接,此時(shí)Client(127.0.0.1:40541)保持連接,如圖27所示, localhost.2017 > localhost.40541 Flags [F.],Server發(fā)送FIN給Client,結(jié)合圖26和圖28,Server的TCP狀態(tài)轉(zhuǎn)移過(guò)程:ESTABLISHED -> FIN_WAIT1 -> FIN_WAIT2,Client的TCP狀態(tài)由ESTABLISHED轉(zhuǎn)移為CLOSE_WAIT。
如圖27紅色方框所示,因?yàn)镃lient(localhost:40541)發(fā)給Server的FIN被防火墻丟棄,所以Client收不到Server發(fā)送的FIN ACK,從localhost.40541 > localhost.2017 Flags [F.]行記錄看,Client重試7次,重試時(shí)間間隔:1s、1s、1s、4s、6s、13s、26s。
設(shè)置防火墻丟棄Client(127.0.0.1:40541)發(fā)送出去的包,緊接著Client主動(dòng)斷開(kāi)連接,結(jié)合圖26和圖28,Client的TCP狀態(tài)由CLOSE_WAIT轉(zhuǎn)移為L(zhǎng)AST_ACK,因?yàn)镾erver(127.0.0.1:2017)接收不到Client發(fā)送的FIN(已被防火墻丟棄),也就不能給Client發(fā)送FIN ACK,LAST_ACK不能直接轉(zhuǎn)移為CLOSED,超時(shí)后LAST_ACK -> CLOSED,超時(shí)時(shí)長(zhǎng)對(duì)應(yīng)圖27的重試時(shí)長(zhǎng)。

圖27 模擬出現(xiàn)LAST-ACK(tcpdump)
圖28 TCP連接狀態(tài)LAST-ACK等待時(shí)長(zhǎng)(netstat)

參考文檔

tcp 三次握手和四次斷連深入分析:連接狀態(tài)和socket API的關(guān)系

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1、TCP狀態(tài)linux查看tcp的狀態(tài)命令:1)、netstat -nat 查看TCP各個(gè)狀態(tài)的數(shù)量2)、lso...
    北辰青閱讀 9,739評(píng)論 0 11
  • 最近在惡補(bǔ)計(jì)算機(jī)網(wǎng)絡(luò)方面的知識(shí),之前對(duì)于TCP的三次握手和四次分手也是模模糊糊,對(duì)于其中的細(xì)節(jié)更是渾然不知,最近看...
    微醺歲月閱讀 9,669評(píng)論 4 128
  • 1 概述 要了解三次握手&四次揮手的過(guò)程,就需要對(duì)TCP的報(bào)頭以及有限狀態(tài)機(jī)的概念有所了解,本文將介紹TCP報(bào)頭的...
    ghbsunny閱讀 1,217評(píng)論 0 3
  • 三次握手,建立連接 第一次握手:建立連接時(shí),客戶端A發(fā)送SYN包(SYN=j)到服務(wù)器B,并進(jìn)入SYN_SEND狀...
    彬哲閱讀 728評(píng)論 0 0
  • 每天早起,閱讀,喝水,運(yùn)動(dòng),吃早餐,上班,做家務(wù),陪伴孩子,感覺(jué)生活過(guò)得很充實(shí),有多自律就有多自由,我會(huì)一直努力的。
    可愛(ài)的純子閱讀 435評(píng)論 2 1

友情鏈接更多精彩內(nèi)容