[基礎] 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通常都會分開發送。html

TCP可靠傳輸的實現

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

發送緩存用來暫時存放: 發送應用程序傳送給發送方 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的狀況)設計的,對於公網不宜使用。網絡

拓展閱讀

相關文章
相關標籤/搜索