天天看點

Linux——TCP協定(三次握手(從資料包名,雙方連接配接狀态,包序管理分析),四次揮手(從資料包名,雙方連接配接狀态分析))前言1. 三次握手2. 四次揮手

文章目錄:

  • 前言
  • 1. 三次握手
    • 1.1 從資料包名稱和連接配接雙方狀态分析三次握手
    • 1.2 包序管理
      • 1.2.1 抓網絡資料包
      • 1.2.2 分析TCP網絡資料包
      • 1.2.3 分析TCP包序号
  • 2. 四次揮手

前言

TCP協定的連接配接是面向連接配接,可靠傳輸,面向位元組流的,而TCP之是以能保持可靠傳輸是因為三次握手和四次揮手

1. 三次握手

1.1 從資料包名稱和連接配接雙方狀态分析三次握手

首先我們通過資料包名和連接配接雙方的連接配接狀态來了解三次握手的過程

如下圖所示

Linux——TCP協定(三次握手(從資料包名,雙方連接配接狀态,包序管理分析),四次揮手(從資料包名,雙方連接配接狀态分析))前言1. 三次握手2. 四次揮手

用戶端在發送SYN資料包後用戶端的狀态變為SYN_SENT,當服務端接收到用戶端發送的SYN資料包後,服務端的狀态變為SYN_RECV,當用戶端接收到服務端的SYN資料包和ACK資料包後,用戶端的狀态變為ESTABLISHED,當服務端接收到用戶端發送的ACK資料包時,服務端的狀态變為ESTABLISHED,此時用戶端和服務端已完成三次握手,即建立了雙向連接配接。

【問題一】為什麼要三次握手,而兩次握手不行呢?

三次握手可以保證通信雙方都是有連接配接的,若隻進行兩次連接配接服務端回複用戶端的應答并發送SYN資料報,用戶端不作出回應,并不能確定用戶端接收到來自服務端的資料,是以兩次握手隻能保證用戶端到服務端的連接配接是有效的,并不能保證服務端到用戶端的連接配接有效

1.2 包序管理

1.2.1 抓網絡資料包

  • 在windows平台下,可以使用wireshark軟體來抓取網絡資料包

  • 在linux平台下,使用 tcpdump 來抓取網絡資料,TCP和UDP的包都可以使用此指令抓取

    1)萬能公式:tcpdump -i any port [端口] -s 0 -w xxx.dat

    2)使用root使用者進行抓包

    如上2)中的指令是對某個端口進行抓包,并将結果放入xxx.dat中,但我們不能直接對xxx.dat中的資料進行分析,需要借助windows平台下的wireshark軟體來分析

1.2.2 分析TCP網絡資料包

我們之前單程序的用戶端和服務端的代碼,使用tcpdump抓包分析三次握手的過程

首先在root使用者下輸入 tcpdump -i any port 18989 -s 0 -w 123.dat ,先進行抓包,再讓用戶端服務端程式跑起來,防止三次握手結束,沒有抓到,讓用戶端服務端跑一會後終止程式,我們可以看到如下圖所示:

Linux——TCP協定(三次握手(從資料包名,雙方連接配接狀态,包序管理分析),四次揮手(從資料包名,雙方連接配接狀态分析))前言1. 三次握手2. 四次揮手

可以看到一共抓到了64個資料包,并産生了一個123.dat檔案,通過Xftp将123.dat傳到windows下使用wireshark進行分析,如下圖所示:

Linux——TCP協定(三次握手(從資料包名,雙方連接配接狀态,包序管理分析),四次揮手(從資料包名,雙方連接配接狀态分析))前言1. 三次握手2. 四次揮手

1.2.3 分析TCP包序号

為什麼TCP需要包序号?

本質上是為了維護可靠傳輸,用戶端維護了一套序号,服務端也維護了一套序号
  • client–>server:消耗(seq)的是用戶端維護的序号,服務端告訴用戶端自己收到資料的時候,是确認(ACK)用戶端的序号
  • server–>client:消耗(seq)的是服務端維護的序号,用戶端告訴服務端自己收到資料的時候,是确認(ACK)服務端的序号

觀察如下圖所示抓到的包,分析TCP包序号的變化

Linux——TCP協定(三次握手(從資料包名,雙方連接配接狀态,包序管理分析),四次揮手(從資料包名,雙方連接配接狀态分析))前言1. 三次握手2. 四次揮手

分析如下圖所示:

Linux——TCP協定(三次握手(從資料包名,雙方連接配接狀态,包序管理分析),四次揮手(從資料包名,雙方連接配接狀态分析))前言1. 三次握手2. 四次揮手
  • 純ACK資料包不消耗序号
  • TCP資料也消耗序号,一個位元組消耗一個序号
  • 确認序号ACK=消息發送方的序号+資料長度
  • 确認序号的作用是:告知消息發送方,期望下次發送資料從哪一個序号開始發送

    注意:TCP三向交握中,協商雙方的其實序号并不一定是從0号序号開始,可以從任意位置開始,隻要雙方協商好就行

    TCP可靠的原因就在于序号,丢了那個資料都可以知道,因為一個位元組占一個序号

2. 四次揮手

通過資料包名和連接配接雙方的狀态分析四次揮手的過程如下圖所示:

Linux——TCP協定(三次握手(從資料包名,雙方連接配接狀态,包序管理分析),四次揮手(從資料包名,雙方連接配接狀态分析))前言1. 三次握手2. 四次揮手
資料傳輸完畢以後,雙方都可以釋放連接配接。在最開始的時候,用戶端與伺服器都是處于ESTABLISHED狀态,如果用戶端主動關閉,則服務端被動關閉;若服務端主動關閉,則用戶端被動關閉;
MSL:封包的最大生存時間,在傳輸中,一個封包的最大生命周期就是一個MSL時間,過了這個時間該封包就會被丢棄

【問題一】主動斷開方為TIME_WAIT狀态為什麼要等待2MSL後才變為CLOSED狀态呢?

當主動斷開方為TIME_WAIT狀态時,如果主動斷開方發送的ACK資料包丢失,則過MSL後,被動連接配接方會重新發送FIN資料包,讓主動斷開連接配接方重新發送ACK資料包,若主動斷開連接配接方MSL後将狀态變為CLOSED則無法收到重發的FIN資料包,無法重新發送ACK資料包
Linux——TCP協定(三次握手(從資料包名,雙方連接配接狀态,包序管理分析),四次揮手(從資料包名,雙方連接配接狀态分析))前言1. 三次握手2. 四次揮手
  • 2MSL = 丢失的ACK的MSL + 重傳的FIN的MSL
本質就是為了讓主動斷開連接配接方能夠接收到被動斷開連接配接方重傳的FIN封包
  • TIME_WAIT狀态存在于主動斷開連接配接方

如果服務端位于主動斷開連接配接方,此時一定在四次握手的過程中擁有TIME_WAIT狀态;即使當服務端程序已經結束了,但是服務端之前使用TCP協定針對針對的連接配接還是TIME_WAIT狀态,換句話說,服務端之前綁定的端口還沒有被網絡協定棧的TCP協定釋放掉,導緻服務端無法快速的重新開機(端口已被站用問題)

解決辦法:使用setsockopt函數,讓端口重用

int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);

  • sockfd:偵聽套接字listen_fd
  • level:SOL_SOCKET 套接字選項
  • optname:SO_REUSEADDR 重用端口(隻有當服務端的狀态為TIME_WAIT時才能重用)
  • optval:1
  • optlen:opt的大小

假設用戶端主動斷開連接配接,服務端被動斷開連接配接

1)用戶端程序發出連接配接釋放封包FIN=1,并且停止發送資料。此時,用戶端進入FIN-WAIT1(終止等待1)狀态。這時候用戶端處于一個半關閉的狀态,即用戶端已經沒有資料需要發送了,但是伺服器若要發送資料,用戶端依然需要接受。

2)伺服器收到連接配接釋放封包後,發送确認封包ACK=1。此時,服務端就進入了CLOSE-WAIT(關閉等待)狀态。進入CLOSE_WAIT後說明伺服器準備關閉連接配接。

3)用戶端收到伺服器的确認請求之後,此時用戶端就進入了FIN-WAIT2(終止等待2)狀态,等待伺服器發送連接配接釋放封包。(在這個之前還需要接受伺服器發送的最後的資料)。

4)當伺服器真正調用close關閉連接配接時, 會向用戶端發送FIN=1, 此時伺服器進入LAST_ACK(最後确認)狀态, 等待用戶端的最後一次ACK回複。

5)用戶端收到伺服器的連結釋放封包之後,必須發出确認封包ACK=1。此時,用戶端就進入了TIME-WAIT(時間等待)狀态,等待使用者關閉套接字。注意此時TCP連結還沒有釋放,必須經過2*MSL(封包最大生命周期)的時間後,當用戶端撤銷相應的TCP後,才進入CLOSED狀态。

6)伺服器隻要收到用戶端發出的确認,徹底關閉連接配接,立即就進行CLOSED狀态,于是就結束了這次的TCP連接配接。可以看到,伺服器結束TCP連接配接的時間要比用戶端早一些。

繼續閱讀