TCP/IP之TCP協議首部、三次握手、四次揮手、FSM
TCP包頭
<--------------------------------32 位------------------------------> 0 8 16 24 32 |----------------|----------------|----------------|----------------| ----- | Source port | Destination port | | |-------------------------------------------------------------------| | | 序 號 | |-------------------------------------------------------------------| 20字節 | 確 認 號 | 固 定 |-------------------------------------------------------------------| 首 部 | | | U A P R S F| | |數據偏移 | 保 留 | R C S S Y I| 窗口 | | | | G K H T N N| | | |-------------------------------------------------------------------| | | 校 驗 和 | 緊急指針 | |-------------------------------------------------------------------| ----- | 選 項 (長 度 可 變) | 填充 | ---------------------------------------------------------------------
TCP包頭
源端口、目標端口 :計算機上的進程要和其他進程通信是要通過計算機端口的,而一個計算機端口某個時刻只能被一個進程占用,所以通過指定源端口和目標端口,就可以知道是哪兩個進程需要通信。源端口、目標端口是用16位表示的,可推算計算機的端口個數為2^16個。
序列號 :表示本報文段所發送數據的第一個字節的編號。在TCP連接中所傳送的字節流的每一個字節都會按順序編號。由於序列號由32位表示,所以每2^32個字節,就會出現序列號回繞,再次從 0 開始。
確認號 :表示接收方期望收到發送方下一個報文段的第一個字節數據的編號。也就是告訴發送方:我希望你(指發送方)下次發送的數據的第一個字節數據的編號為此確認號。
數據偏移 :表示TCP報文段的首部長度,共4位,由於TCP首部包含一個長度可變的選項部分,需要指定這個TCP報文段到底有多長。它指出 TCP 報文段的數 據起始處距離 TCP 報文段的起始處有多遠。該字段的單位是32位(即4個字節為計算單位),4位二進制最大表示15,所以數據偏移也就是TCP首部最大60字節。
URG :表示本報文段中發送的數據是否包含緊急數據。後面的緊急指針字段(urgent pointer)只有當URG=1時才有效。
ACK :表示是否前面確認號字段是否有效。只有當ACK=1時,前面的確認號字段才有效。TCP規定,連接建立後,ACK必須為1,帶ACK標誌的TCP報文段稱為確認報文段。
PSH :提示接收端應用程序應該立即從TCP接收緩沖區中讀走數據,為接收後續數據騰出空間。如果為1,則表示對方應當立即把數據提交給上層應用,而不是緩存起來,如果應用程序不將接收到的數據讀走,就會一直停留在TCP接收緩沖區中。
RST :如果收到一個RST=1的報文,說明與主機的連接出現了嚴重錯誤(如主機崩潰),必須釋放連接,然後再重新建立連接。或者說明上次發送給主機的數據有問題,主機拒絕響應,帶RST標誌的TCP報文段稱為復位報文段。
SYN :在建立連接時使用,用來同步序號。當SYN=1,ACK=0時,表示這是一個請求建立連接的報文段;當SYN=1,ACK=1時,表示對方同意建立連接。SYN=1,說明這是一個請求建立連接或同意建立連接的報文。只有在前兩次握手中SYN才置為1,帶SYN標誌的TCP報文段稱為同步報文段。
FIN :表示通知對方本端要關閉連接了,標記數據是否發送完畢。如果FIN=1,即告訴對方:“我的數據已經發送完畢,你可以釋放連接了”,帶FIN標誌的TCP報文段稱為結束報文段。
窗口大小 :表示現在允許對方發送的數據量,也就是告訴對方,從本報文段的確認號開始允許對方發送的數據量,達到此值,需要ACK確認後才能再繼續傳送後面數據,由Window size value * Window size scaling factor(此值在三次握手階段TCP選項Window scale協商得到)得出此值。
校驗和 :提供額外的可靠性。
緊急指針 :標記緊急數據在數據字段中的位置。
選項部分 :其最大長度可根據TCP首部長度進行推算。TCP首部長度用4位表示,選項部分最長為:(2^4-1)*4-20=40字節。
常見選項:
最大報文段長度:Maxium Segment Size,MSS,通常1460字節
窗口擴大:Window Scale
時間戳: Timestamps
1 最大報文段長度MSS(Maximum Segment Size) 指明自己期望對方發送TCP報文段時那個數據字段的長度。比如:1460字節。數據字段的長度加上TCP首部的長度才等於整個TCP報文段的長度。 MSS不宜設的太大也不宜設的太小。若選擇太小,極端情況下,TCP報文段只含有1字節數據,在IP層傳輸的數據報的開銷至少有40字節(包括TCP報文段的首部和IP數據報的首部)。這樣,網絡的利用率就不會超過1/41。若TCP報文段非常長,那麽在IP層傳輸時就有可能要分解成多個短數據報片。在終點要把收到的各個短數據報片裝配成原來的TCP報文段。當傳輸出錯時還要進行重傳,這些也都會使開銷增大。因此MSS應盡可能大,只要在IP層傳輸時不需要再分片就行。在連接建立過程中,雙方都把自己能夠支持的MSS寫入這一字段。MSS只出現在SYN報文中。即:MSS出現在SYN=1的報文段中。
- MTU和MSS值的關系:MTU=MSS+IP Header+TCP Header
- 通信雙方最終的MSS值=較小MTU-IP Header-TCP Header
2 窗口擴大
為了擴大窗口,由於TCP首部的窗口大小字段長度是16位,所以其表示的最大數是65535。但是隨著時延和帶寬比較大的通信產生(如衛星通信),需要更大的窗口來滿足性能和吞吐率,所以產生了這個窗口擴大選項。3 時間戳
可以用來計算RTT(往返時間),發送方發送TCP報文時,把當前的時間值放入時間戳字段,接收方收到後發送確認報文時,把這個時間戳字段的值復制到確認報文中,當發送方收到確認報文後即可計算出RTT。也可以用來防止回繞序號PAWS,也可以說可以用來區分相同序列號的不同報文。因為序列號用32為表示,每2^32個序列號就會產生回繞,那麽使用時間戳字段就很容易區分相同序列號的不同報文。
映射第四層到應用程序
TCP協議PORT
- 傳輸層通過port號,確定應用層協議
- Port number:
- tcp:傳輸控制協議,面向連接的協議;通信前需要建立虛擬鏈路;結束後拆除鏈路。
0-65535 - udp:User Datagram Protocol,無連接的協議。
0-65535
- tcp:傳輸控制協議,面向連接的協議;通信前需要建立虛擬鏈路;結束後拆除鏈路。
- IANA:互聯網數字分配機構(負責域名,數字資源,協議分配)
0-1023:系統端口或特權端口(僅管理員可用) ,眾所周知,永久的分配給固定的系統應用使用,22/tcp(ssh), 80/tcp(http), 443/tcp(https)
1024-49151:用戶端口或註冊端口,但要求並不嚴格,分配給程序註冊為某應用使用,1433/tcp(SqlServer), 1521/tcp(oracle),3306/tcp(mysql),11211/tcp/udp(memcached)
49152-65535:動態端口或私有端口,客戶端程序隨機使用的端口。
其範圍的定義:/proc/sys/net/ipv4/ip_local_port_range
TCP三次握手
握手 | 客戶端狀態 | 客戶端TCP | 方向 | 服務器TCP | 服務器狀態 | 描述 |
---|---|---|---|---|---|---|
第一次 | 初始:CLOSED 發送:SYN-SENT |
SYN=1, seq=x | ----> | 初始:LISTEN 接收:SYN-RECEIVED |
同步(SYN) | |
第二次 | 接收:SYN-SENT | <---- | SYN=1, ACK=1, seq=y, ack=x+1 | 發送:SYN-RECEIVED | 同步、確認(ACK) | |
第三次 | 發送:ESTABLISHED | ACK=1, seq=x+1, ack=y+1 | ----> | 接收:ESTABLISHED | 確認(ACK) | |
數據傳輸 | <---> |
TCP四次揮手
揮手 | 客戶端狀態 | 客戶端TCP | 方向 | 服務器TCP | 服務器狀態 | 描述 |
---|---|---|---|---|---|---|
第一次 | 初始:ESTABLISHED 發送:FIN-WAIT-1 |
FIN=1, seq=u | ----> | 初始:ESTABLISHED 接收:CLOSE-WAIT |
分手(FIN) | |
第二次 | 接收:FIN-WAIT-2 | <---- | ACK=1, seq=v, ack=u+1 | 發送:CLOSE-WAIT | 好的(ACK) | |
第三次 | 接收:FIN-WAIT-2 | <---- | FIN=1, ACK=1, seq=w, ack=u+1 | 發送:LAST-ACK | 分手(FIN+ACK) | |
第四次 | 發送:TIME-WAIT 2MSL後:CLOSED |
ACK=1, seq=u+1, ack=w+1 | ----> | 接收:CLOSED | 好的(ACK) |
有限狀態機FSM :Finite State Machine
FSM | 說明 |
---|---|
CLOSED | 沒有任何連接狀態 |
LISTEN | 偵聽狀態,等待來自遠方TCP端口的連接請求 |
SYN-SENT | 在發送連接請求後,等待對方確認 |
SYN-RECEIVED | 在收到和發送一個連接請求後,等待對方確認 |
ESTABLISHED | 代表傳輸連接建立,雙方進入數據傳送狀態 |
FIN-WAIT-1 | 主動關閉,主機已發送關閉連接請求,等待對方確認 |
FIN-WAIT-2 | 主動關閉,主機已收到對方關閉傳輸連接確認,等待對方發送關閉傳輸連接請求 |
TIME-WAIT | 完成雙向傳輸連接關閉,等待所有分組消失 |
CLOSE-WAIT | 被動關閉,收到對方發來的關閉連接請求,並已確認 |
LAST-ACK | 被動關閉,等待最後一個關閉傳輸連接確認,並等待所有分組消失 |
CLOSING | 雙方同時嘗試關閉傳輸連接,等待對方確認 |
客戶端先發送一個FIN給服務端,自己進入了FIN_WAIT_1狀態,這時等待接收服務端的報文,該報文會有三種可能:
- 只有服務端的ACK
- 只有服務端的FIN
- 基於服務端的ACK,又有FIN
1、只收到服務器的ACK,客戶端會進入FIN_WAIT_2狀態,後續當收到服務端的FIN時,回應發送一個ACK,會進入到TIME_WAIT狀態,這個狀態會持續2MSL(TCP報文段在網絡中的最大生存時間,RFC 1122標準的建議值是2min)。客戶端等待2MSL,是為了當最後一個ACK丟失時,可以再發送一次。因為服務端在等待超時後會再發送一個FIN給客戶端,進而客戶端知道ACK已丟失。
2、只有服務端的FIN時,回應一個ACK給服務端,進入CLOSING狀態,然後接收到服務端的ACK時,進入TIME_WAIT狀態。
3、同時收到服務端的ACK和FIN,直接進入TIME_WAIT狀態。
客戶端的典型狀態轉移
- 客戶端通過connect系統調用主動與服務器建立連接connect系統調用首先給服務器發送一個同步報文段,使連接轉移到SYN_SENT狀態。
- 此後connect系統調用可能因為如下兩個原因失敗返回:
1、如果connect連接的目標端口不存在(未被任何進程監聽),或者該端口仍被處於TIME_WAIT狀態的連接所占用(見後文),則服務器將給客戶端發送一個復位報文段,connect調用失敗。
2、如果目標端口存在,但connect在超時時間內未收到服務器的確認報文段,則connect調用失敗。
connect調用失敗將使連接立即返回到初始的CLOSED狀態。如果客戶端成功收到服務器的同步報文段和確認,則connect調用成功返回,連接轉移至ESTABLISHED狀態。
當客戶端執行主動關閉時,它將向服務器發送一個結束報文段,同時連接進入FIN_WAIT_1狀態。若此時客戶端收到服務器專門用於確認目的的確認報文段,則連接轉移至FIN_WAIT_2狀態。當客戶端處於FIN_WAIT_2狀態時,服務器處於CLOSE_WAIT狀態,這一對狀態是可能發生半關閉的狀態。此時如果服務器也關閉連接(發送結束報文段),則客戶端將給予確認並進入TIME_WAIT狀態。
客戶端從FIN_WAIT_1狀態可能直接進入TIME_WAIT狀態(不經過FIN_WAIT_2狀態),前提是處於FIN_WAIT_1狀態的服務器直接收到帶確認信息的結束報文段(而不是先收到確認報文段,再收到結束報文段)。
處於FIN_WAIT_2狀態的客戶端需要等待服務器發送結束報文段,才能轉移至TIME_WAIT狀態,否則它將一直停留在這個狀態。如果不是為了在半關閉狀態下繼續接收數據,連接長時間地停留在IN_WAIT_2狀態並無益處。連接停留在FIN_WAIT_2狀態的情況可能發生在:客戶端執行半關閉後,未等服務器關閉連接就強行退出了。此時客戶端連接由內核來接管,可稱之為孤兒連接(和孤兒進程類似)。
Linux為了防止孤兒連接長時間存留在內核中,定義了兩個內核參數:
- /proc/sys/net/ipv4/tcp_max_orphans 指定內核能接管的孤兒連接數目
- /proc/sys/net/ipv4/tcp_fin_timeout 指定孤兒連接在內核中生存的時間
有限狀態機
TCP協議中的三次握手和四次揮手
客戶機端的三次握手和四次揮手、服務器端的三次握手和四次揮手
graph TB C(CLOSED) S(SYN_SENT) E(ESTABLISHED) F1(FIN_WAIT_1) F2(FIN_WAIT_2) T(TIME_WAIT) SC(CLOSED) L(LISTEN) SR(SYN_RCVD) SE(ESTABLISHED) CW(CLOSE_WAIT) LA(LAST_ACK) subgraph 客戶機端的三次握手和四次揮手 C --"發送SYN"--> S S --"接收SYN和ACK,發送ACK"--> E E --"發送FIN"--> F1 F1 --"接收確認,不發送任何信息"--> F2 F2 --"接收FIN,發送ACK"--> T T --"等待30秒"--> C end subgraph 服務器端的三次握手和四次揮手 SC --監聽--> L L --"接收SYN,發送SYN和ACK"--> SR SR --"接收ACK,不發送任何信息"--> SE SE --"接收FIN,發送ACK"--> CW CW --"發送FIN"--> LA LA --"接收ACK,不發送任何信息"--> SC endTCP/IP之TCP協議首部、三次握手、四次揮手、FSM