1 理論知識
先上一張圖,TCP/IP詳解第18章的這張圖描述了一個正常的三次握手和四次揮手的狀態遷移,以及seq、ack序號的變化。
基本狀態看圖就能了解,本文主要圍繞序號的變化進行講解。
1)seq序號
seq的初始值在不同系統實現不一樣,一般為隨時間增長的值。當seq超過4字節存儲空間后從0開始。
在某個方向上傳輸N個字節的數據,序列號就+N,因此seq用於確認在某個方向上傳輸的字節數。
如果傳輸的數據字節為0,即只有首部,那序列號還增加嗎?當syn 或 fin被置1,雖然報文里長度為0,計算ACK時我們可以認為其長度為1。其他情況(如只有ack)不增加。
2)ack序列號
只有ack標志置1才有效。在TCP交互的整個周期,除了syn包外其他所有包ack都被置1。
ack序列號是上個報文的序列號+已經成功收到數據長度,比如上次成功接收了seq為1,長度為1000的數據,發送的ack序號為1001,表示我seq1000以前的數據我已經成功接收了,我對序列號1001開始的數據感興趣。
2 實例
這是一次http交互的一個例子。①~③為三次握手,④~⑥為數據傳送階段,⑦~⑨為四次揮手過程(這里實際只有三個包,因為⑧結合了fin和ack兩個步驟)。
我們看第一個包的seq和ack都是0,這是wireshark為了方便閱讀將序號重0開始計算(沒有改變實際值),我們看到有字段里有兩個帶RAW的字段,它們才是序號真實的值。
注:
1)在下面的解析中,我們不會使用raw的值,但為了說明不是從0開始,我們取整數10000和20000作為兩端seq的初始值。
2)192.168.31.21記為A,211.158.235.30記為B。
2.1 三次握手過程
序號變化(假設從10000和20000作為初始值):
① 首先A向B發一個SYN包,告訴B請求建立連接。seq為初始化的隨機值(如何初始化和系統具體實現有關),這里假設為10000,此時ACK序號為0。
② B收到后會發一個對SYN包的確認包(SYN+ACK)回去,表示對第一個SYN包的確認,並繼續握手操作。
此時B也以一個隨機值來初始化seq(與A無關),這里假設為20000。B的ACK是A的seq加1,即10000+1=10001。表示你的請求我已收到,我這方的序號就從20000開始,由於你的syn包消耗了1字節,我期望下一個收到的序列號從10001開始。
③ A收到SYN+ACK 包后回一個確認包(ACK)通知B連接已建立。
它的seq是上個請求(①)的seq加1,即10000+1=10001,當然也等於上一個包(②)的ACK,ACK是B的seq加1,即20000+1=20001,用於確認收到②(syn被置位,所以也消耗了1字節)。
至此,三次握手完成,一個TCP連接建立完成。
2.2 數據傳輸階段
④ A發起Get請求,由於上一個③的len=0,且沒有syn或fin標志,因此沒有消耗seq的值,seq不變還是10001。ACK序號確認的還是上一個收到的②(這段期間也沒有收到新的包)。
⑤ B對收到的④發出回應,告訴A我已經收到。由於上一個請求②消耗了1字節,這里seq為20001。ACK為④的seq+len=10001+183=10184。
⑥ B發送200ok,由於⑤長度為0沒有消耗seq,這里seq仍然為20001,ACK序號確認的還是上一個收到的④。
2.3 四次揮手
計算過程基本相同,幾個注意點是:
1)A對⑥的確認合並到第一個揮手包里了。
2)⑧將FIN和ACK原本的兩次揮手合並。
⑦ 確認⑥,並發出揮手。
⑧ 確認⑦,並發出揮手。⑦長度0,但是有FIN標志,消耗1字節,ACK=10184+1=10185。
⑨ 確認⑧,雖然⑧長度為0,但是有FIN標志,消耗1字節,ACK=20155+1=20156。至此揮手完成,TCP整個生命周期結束。