06.26 TCP的seq和ack號計算方法

seq和ack號存在於TCP報文段的首部中,seq是序號,ack是確認號,大小均為4字節(注意與大寫的ACK不同,ACK是6個控制位之一,大小隻有一位, 僅當 ACK=1 時ack字段才有效。建立 TCP 連接後,所有報文段都必須把 ACK 字段置為 1。)

seq:佔 4 字節,序號範圍[0,2^32-1],序號增加到 2^32-1 後,下個序號又回到 0。TCP 是面向字節流的,通過 TCP 傳送的字節流中的每個字節都按順序編號,而報頭中的序號字段值則指的是本報文段數據的第一個字節的序號

ack:佔 4 字節,期望收到對方下個報文段的第一個數據字節的序號。

1、三次握手過程中seq和ack的值:

一個TCP連接的建立是通過三次握手來實現的

1. (A) –> [SYN] –> (B)

假如服務器B和客戶機A通訊. 當A要和B通信時,A首先向B發一個SYN (Synchronize) 標記的包,告訴B請求建立連接.

注意: 一個 SYN包就是僅SYN標記設為1的TCP包(參見TCP包頭Resources). 認識到這點很重要,只有當B受到A發來的SYN包,才可建立連接,除此之外別無他法。因此,如果你的防火牆丟棄所有的發往外網接口的SYN包,那麼你將不能主動連接外部任何主機,除非不是TCP協議。

2. (A)

接著,B收到後會發一個對SYN包的確認包(SYN/ACK)回去,表示對第一個SYN包的確認,並繼續握手操作.

注意: SYN/ACK包是僅SYN 和 ACK 標記為1的包.

3. (A) –> [ACK] –> (B)

A收到SYN/ACK 包,A發一個確認包(ACK),通知B連接已建立。至此,三次握手完成,一個TCP連接完成

Note: ACK包就是僅ACK 標記設為1的TCP包. 需要注意的是當三此握手完成、連接建立以後,TCP連接的每個包都會設置ACK位

握手階段:

序號方向seqackSYNACK1A->B100000102B->A2000010000+1=10001113A->B1000120000+1=2000101

解釋:

1:A向B發起連接請求,以一個隨機數初始化A的seq,這裡假設為10000,此時ACK=0

2:B收到A的連接請求後,也以一個隨機數初始化B的seq,這裡假設為20000,意思是:你的請求我已收到,我這方的數據流就從這個數開始。B的ACK是A的seq加1,即10000+1=10001

3:A收到B的回覆後,它的seq是它的上個請求的seq加1,即10000+1=10001,意思也是:你的回覆我收到了,我這方的數據流就從這個數開始。A此時的ACK是B的seq加1,即20000+1=20001

2、數據傳輸過程中seq和ack的值:

序號方向seqacksize23A->B4000070000

1514

24B->A70000

40000+1514-54=41460

54

25A->B4146070000+54-54=70000151426B->A7000041460+1514-54=42920

54

解釋:

23:B接收到A發來的seq=40000,ack=70000,size=1518的數據包

24:於是B向A也發一個數據包,告訴A,你的上個包我收到了。A的seq就以它收到的數據包的ack填充,ack是它收到的數據包的seq加上數據包的大小(不包括:以太網協議頭=14字節,IP頭=20字節,TCP頭=20字節),以證實B發過來的數據全收到了。

25:A在收到B發過來的ack為41460的數據包時,一看到41460,正好是它的上個數據包的seq加上包的大小,就明白,上次發送的數據包已安全到達。於是它再發一個數據包給B。這個正在發送的數據包的seq也以它收到的數據包的ack填充,ack 就以它收到的數據包的seq(70000)加上包的size(54)填充,即ack=70000+54-54(全是頭長,沒數據項)。

減去54的原因見下圖(鏈路層使用的是Ethernet II 格式,這個格式有14字節以太網首部+4字節以太網尾部):

應用數據=size-14-20-20=size-54。(假設IP首部和TCP首部都沒有可選選項)

為什麼不減去以太網尾部的4字節呢?

因為在物理層上網卡要先去掉前導同步碼和幀開始定界符,然後對幀進行CRC檢驗,如果幀校驗和錯,就丟棄此幀。如果校驗和正確,就判斷幀的目 的硬件地址是否符合自己的接收條件(目的地址是自己的物理硬件地址、廣播地址、可接收的多播硬件地址等),如果符合,就將幀交“設備驅動程序”做進一步處 理。這時我們的抓包軟件才能抓到數據,因此,抓包軟件抓到的是去掉前導同步碼、幀開始分界符、FCS之外的數據,

3、四次揮手過程中seq和ack的值:

TCP連接的結束是四次揮手的過程,ACK一直等於1

序號方向seqackFINACK1A->B8000090000112B->A9000080000+1=80001013B->A9500080001114A->B8000195000+1=9500101

1. (A) –> [FIN/ACK] –> (B)

客戶端A沒有要發送給服務端B的數據了,想要關閉鏈接,則發送一個FIN=1,ACK=1的包,告訴B可以關閉連接了,我沒有什麼數據要給你了。

2. (A)

然後B會發送ACK=1的包給A,告訴A我知道你沒有什麼想給我的了,但是我還有數據要給你,你先等下,我先不想FINISH呢。

3. (A)

等B把數據都發送給A之後,B會再次發送一個包,這次FIN=1,表示我這邊也想關閉了,咱倆一起關把。在2和3之間,可能還會有很多B->A的傳遞,ack均為80001。

4. (A) –> [ACK] –> (B)

然後A回應一個ACK,表示我知道了,一起關吧。B收到這個ACK後,就會CLOSE。但是實際上A不會直接CLOSE,還會進入一個等待時間狀態TIME_WAIT,持續2倍的MSL(Maximum Segment Lifetime,報文段在網絡上能存活的最大時間)。過了這個狀態,才會CLOSE。為什麼要等待一段時間?原因有二:

(1)保證TCP的全雙工連接能夠可靠關閉

假如A發送的最後一次ACK丟包了,沒有被B收到,那B超時之後,會再次發送一個FIN包,然後這個包被處於TIME_WAIT狀態的A收到,A會再次發送一個ACK包,並重新開始計時,一直循環這個過程,直到A在TIME_WAIT的整個過程中都沒有收到B發過來的FIN包,這說明B已經收到了A的ACK包並CLOSE了,因此A這時候才可以安心CLOSE。如果A沒有TIME_WAIT狀態而是直接close,那麼當ACK丟包之後,B會再次發送一個FIN包,但是這個包不會被A回應,因此B最終會收到RST,誤以為是連接錯誤,不符合可靠連接的要求。因此需要等待ACK報文到達B+BRST是TCP數據報中6個控制位之一,6個控制位的作用如下:

URG 緊急:當 URG=1 時,它告訴系統此報文中有緊急數據,應優先傳送(比如緊急關閉),這要與緊急指針字段配合使用。

ACK 確認:僅當 ACK=1 時確認號字段才有效。建立 TCP 連接後,所有報文段都必須把 ACK 字段置為 1。

PSH 推送:若 TCP 連接的一端希望另一端立即響應,PSH 字段便可以“催促”對方,不再等到緩存區填滿才發送。

RST復位:若 TCP 連接出現嚴重差錯,RST 置為 1,斷開 TCP 連接,再重新建立連接。

SYN 同步:用於建立和釋放連接,稍後會詳細介紹。

FIN 終止:用於釋放連接,當 FIN=1,表明發送方已經發送完畢,要求釋放 TCP 連接。

(2)保證這次連接的重複數據段從網絡中消失

如果A直接close了,然後向B發起了一個新的TCP連接,可能兩個連接的端口號相同。一般不會有什麼問題,但是如果舊的連接有一些數據堵塞了,沒有達到B呢,新的握手連接就已經到B了,那麼這時候,由於區分不同TCP連接是依據套接字,因此B會將這批遲到的數據認為是新的連接的數據,導致數據混亂(源IP地址和目的IP地址以及源端口號和目的端口號的組合稱為套接字,新舊連接的套接字很有可能相同)

總結:

確認號:

在握手和結束時確認號應該是對方序列號加1,傳輸數據時則是對方序列號加上對方攜帶應用層數據的長度,如果對方攜帶應用層數據長度為0,則ack與對方序列號相同,不要+1,比如25的ack與24的seq相同。也可以這樣理解,因為24沒有發送數據,所以A期待B下次發送過來的第一個字節的序號不變,因此25的ack與23的ack相同。

序列號:

在握手和結束時序列號應該是上次序列號+1,傳輸數據時則是上次的序列號加上上次應用層數據發送長度,如果數據長度為0,則seq與上次一樣,不要+1,比如26的seq和24的seq相同。

TCP的seq和ack號計算方法


分享到:


相關文章: