[基礎] TCP小結

抓包示例

root@python:~# tcpdump -n -S  tcp port 5009 # -S 參數的目的是獲得ack的絕對值,不加該參數,第三次握手的ack爲相對值1
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
17:35:43.707223 IP 122.235.85.89.49302 > 172.16.10.164.5009: Flags [S], seq 267775201, win 8192, options [mss 1452,nop,wscale 2,nop,nop,sackOK], length 0   # **客戶端發起請求,第一次握手**
17:35:43.707299 IP 172.16.10.164.5009 > 122.235.85.89.49302: Flags [S.], seq 3046340720, ack 267775202, win 29200, options [mss 1460,nop,nop,sackOK,nop,wscale 7], length 0
17:35:43.729057 IP 122.235.85.89.49302 > 172.16.10.164.5009: Flags [.], ack 3046340721, win 16698, length 0 # **三次握手結束**
17:35:43.778243 IP 122.235.85.89.49302 > 172.16.10.164.5009: Flags [P.], seq 267775202:267775428, ack 3046340721, win 16698, length 226 # **開始傳送數據**
17:35:43.778299 IP 172.16.10.164.5009 > 122.235.85.89.49302: Flags [.], ack 267775428, win 237, length 0
17:35:43.780225 IP 172.16.10.164.5009 > 122.235.85.89.49302: Flags [.], seq 3046340721:3046343625, ack 267775428, win 237, length 2904
17:35:43.780254 IP 172.16.10.164.5009 > 122.235.85.89.49302: Flags [P.], seq 3046343625:3046343827, ack 267775428, win 237, length 202
17:35:43.802585 IP 122.235.85.89.49302 > 172.16.10.164.5009: Flags [.], ack 3046343827, win 16698, length 0
17:35:43.803603 IP 122.235.85.89.49302 > 172.16.10.164.5009: Flags [P.], seq 267775428:267775619, ack 3046343827, win 16698, length 191
17:35:43.803988 IP 172.16.10.164.5009 > 122.235.85.89.49302: Flags [P.], seq 3046343827:3046343878, ack 267775619, win 245, length 51
17:35:43.825824 IP 122.235.85.89.49302 > 172.16.10.164.5009: Flags [P.], seq 267775619:267775895, ack 3046343878, win 16685, length 276
17:35:43.865696 IP 172.16.10.164.5009 > 122.235.85.89.49302: Flags [.], ack 267775895, win 254, length 0
17:35:44.314454 IP 172.16.10.164.5009 > 122.235.85.89.49302: Flags [.], seq 3046343878:3046346782, ack 267775895, win 254, length 2904
17:35:44.314472 IP 172.16.10.164.5009 > 122.235.85.89.49302: Flags [.], seq 3046346782:3046349686, ack 267775895, win 254, length 2904
17:35:44.314477 IP 172.16.10.164.5009 > 122.235.85.89.49302: Flags [.], seq 3046349686:3046352590, ack 267775895, win 254, length 2904
17:35:44.314482 IP 172.16.10.164.5009 > 122.235.85.89.49302: Flags [P.], seq 3046352590:3046352839, ack 267775895, win 254, length 249
17:35:44.336458 IP 122.235.85.89.49302 > 172.16.10.164.5009: Flags [.], ack 3046352839, win 16698, length 0
17:35:44.402718 IP 122.235.85.89.49302 > 172.16.10.164.5009: Flags [P.], seq 267775895:267775926, ack 3046352839, win 16698, length 31
17:35:44.402758 IP 172.16.10.164.5009 > 122.235.85.89.49302: Flags [.], ack 267775926, win 254, length 0
17:35:44.402869 IP 172.16.10.164.5009 > 122.235.85.89.49302: Flags [F.], seq 3046352839, ack 267775926, win 254, length 0   # **四次揮手開始**
17:35:44.404033 IP 122.235.85.89.49302 > 172.16.10.164.5009: Flags [F.], seq 267775926, ack 3046352839, win 16698, length 0
17:35:44.404047 IP 172.16.10.164.5009 > 122.235.85.89.49302: Flags [.], ack 267775927, win 254, length 0
17:35:44.424589 IP 122.235.85.89.49302 > 172.16.10.164.5009: Flags [.], ack 3046352840, win 16698, length 0 # **四次揮手結束**

Flags包標誌:

S=SYN       發起連接標誌。
P=PUSH      傳送數據標誌。
F=FIN       關閉連接標誌。
ACK         表示確認包。
RST=RESET   異常關閉連接。
.           表示沒有任何標誌。

tcp示意圖
tcp狀態轉換圖

TCP連接建立(三次握手)

客戶端A,服務器B,初始序號seq,確認號ack

初始狀態:B處於監聽狀態,A處於打開狀態

A -> B : seq = x (A向B發送連接請求報文段,A進入同步發送狀態SYN-SENT)

B -> A : ack = x + 1,seq = y (B收到報文段,向A發送確認,B進入同步收到狀態SYN-RCVD)

A -> B : ack = y+1 (A收到B的確認後,再次確認,A進入連接狀態ESTABLISHED)

連接後的狀態:B收到A的確認後,進入連接狀態ESTABLISHED

TCP連接釋放(四次揮手)

A -> B : seq = u (A發出連接釋放報文段,進入終止等待1狀態FIN-WAIT-1)

B -> A : ack = u + 1,seq = v (B收到報文段,發出確認,TCP處於半關閉,B還可向A發數據,B進入關閉等待狀態WAIT)

B -> A : ack = u + 1,seq = w (B重複發送確認號,進入最後確認狀態LAST-ACK)

A -> B : ack = w + 1,seq = u + 1 (A發出確認,進入時間等待狀態TIME-WAIT)

經過時間等待計時器設置的時間2MSL後,A才進入CLOSED狀態

爲什麼建立連接是三次握手,而關閉連接卻是四次揮手呢?

三次握手的最主要目的是保證連接是雙工的,可靠更多的是通過重傳機制來保證的。
這是因爲服務端在LISTEN狀態下,收到建立連接請求的SYN報文後,把ACK和SYN放在一個報文裏發送給客戶端。而關閉連接時,當收到對方的FIN報文時,
僅僅表示對方不再發送數據了但是還能接收數據,我們也未必全部數據都發送給對方了,所以我們不可以立即close,也可以發送一些數據給對方後,
再發送FIN報文給對方來表示同意現在關閉連接,因此,我們的ACK和FIN一般都會分開發送。

TCP可靠傳輸的實現

TCP 連接的每一端都必須設有兩個窗口——一個發送窗口和一個接收窗口。TCP 的可靠傳輸機制用字節的序號進行控制。TCP 所有的確認都是基於序號而不是基於報文段。
發送過的數據未收到確認之前必須保留,以便超時重傳時使用。發送窗口沒收到確認不動,和收到新的確認後前移。

發送緩存用來暫時存放: 發送應用程序傳送給發送方 TCP 準備發送的數據;TCP 已發送出但尚未收到確認的數據。
接收緩存用來暫時存放:按序到達的、但尚未被接收應用程序讀取的數據; 不按序到達的數據。

RST標記

RST表示連接重置,用於關閉那些已經沒有必要繼續存在的連接。一般情況下表示異常關閉連接,區別與四次分手正常關閉連接。

產生RST的三個條件是:

  1. 目的地爲某端口的SYN到達,然而在該端口上並沒有正在監聽的服務器;
  2. TCP想取消一個已有連接;
  3. TCP接收到一個根本不存在的連接上的分節;

幾種場景:

  1. 端口未打開,客戶端向服務端某端口發起連接請求SYN,但是目的服務端主機不存在該端口,此時向客戶端迴應RST,中斷連接請求。
  2. 請求超時,常見的有 tw_bucket 滿了、tcp 連接隊列爆滿且開啓 tcp_abort_on_overflow、配置 so_linger、關閉未清空讀緩衝區的連接。
  3. 提前關閉,TCP應用程序接收數據是從操作系統中接收的TCP數據,如果數據到達了操作系統但是我應用數據不想繼續接收數據了,此時RST中斷連接。
  4. 在一個已關閉的socket上收到數據,顯然是異常的,此時應RST中斷連接。

配置與坑

大多都寫開啓net.ipv4.tcp_tw_recycle這個開關,可以快速回收處於TIME_WAIT狀態的socket(針對Server端而言)。
而實際上,這個開關,需要net.ipv4.tcp_timestamps(默認開啓)這個開關開啓纔有效果。
更被提到卻很重要的一個信息是:當tcp_tw_recycle開啓時(tcp_timestamps同時開啓,快速回收socket的效果達到),對於位於NAT設備後面的Client來說,
是一場災難——會導到NAT設備後面的Client連接Server不穩定(有的Client能連接server,有的Client不能連接server)。
也就是說,tcp_tw_recycle這個功能,是爲“內部網絡”(網絡環境自己可控——不存在NAT的情況)設計的,對於公網不宜使用。

拓展閱讀

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章