[TCP]轉TCP相關知識

大搜車-自娛發表於2013-11-05
TCP通訊流程解析
http://blog.csdn.net/phunxm/article/details/5836034


B/S 通訊簡述
整個計算機網路的實現體現為協議的實現, TCP/IP 協議是 Internet 的核心協議, HTTP 協議是比 TCP 更高層次的應用層協議。
HTTP ( HyperText Transfer Protocol ,超文字傳輸協議)是網際網路上應用最為廣泛的一種網路協議。所有的 WWW 檔案都必須遵守這個標準。設計 HTTP 的初衷是為了提供一種釋出和接收 HTML 頁面的方法。
瀏覽器( Web Browser )負責與伺服器建立連線,下載網頁(包括資原始檔及 JS 指令碼檔案)到本地,並最終渲染出頁面。 JS 指令碼檔案執行在客戶端,負責客戶端一些行為響應或預處理,例如提交表單前的資料校驗、滑鼠事件處理等互動。由此可見,瀏覽器( Browser )一方面充當了 C/S 通訊架構中 C 角色 ,另一方面它是 HTML/JavaScript 的解析渲染引擎( Analyze Render Engine )。
在瀏覽器位址列敲入 “http://www.baidu.com/ ” ,按下Enter鍵,瀏覽器中呈現出百度首頁。這樣一種情景我們再熟悉不過,本文通過 wireshark 抓取這一過程的 TCP/IP 資料包,結合 TCP 協議分析 HTTP 通訊的基本流程。
MTU 和 MSS
本文用到的抓包工具為 wireshark ,它的前身是赫赫有名的 Ethereal 。 wireshark 乙太網幀的封包格式為:
Frame = Ethernet Header + IP Header + TCP Header + TCP Segment Data
( 1 ) Ethernet Header = 14 Byte = Dst Physical Address ( 6 Byte ) + Src Physical Address ( 6 Byte ) + Type ( 2 Byte ),乙太網幀頭以下稱之為資料幀 。
( 2 ) IP Header = 20 Byte ( without options field ),資料在 IP 層稱為 Datagram ,分片稱為 Fragment 。
( 3 ) TCP Header = 20 Byte ( without options field ),資料在 TCP 層稱為 Stream ,分段稱為 Segment ( UDP 中稱為 Message ) 。
( 4 ) 54 個位元組後為 TCP 資料負載部分( Data Portion ),即應用層使用者資料。
Ethernet Header 以下的 IP 資料包最大傳輸單位為 MTU ( Maximum Transmission Unit , Effect of short board ),對於大多數使用乙太網的區域網來說, MTU=1500 。
TCP 資料包每次能夠傳輸的最大資料分段為 MSS ,為了達到最佳的傳輸效能,在建立 TCP 連線時雙方協商 MSS 值,雙方提供的 MSS 值的最小值為這次連線的最大 MSS 值。 MSS 往往基於 MTU 計算出來,通常 MSS=MTU-sizeof(IP Header)-sizeof(TCP Header)=1500-20-20=1460 。
這樣,資料經過本地 TCP 層分段後,交給本地 IP 層,在本地 IP 層就不需要分片了。但是在下一跳路由( Next Hop )的鄰居路由器上可能發生 IP 分片!因為路由器的網路卡的 MTU 可能小於需要轉發的 IP 資料包的大小。這時候,在路由器上可能發生兩種情況:
( 1 ) . 如果源傳送端設定了這個 IP 資料包可以分片( May Fragment , DF=0 ),路由器將 IP 資料包分片後轉發。
( 2 ) . 如果源傳送端設定了這個 IP 資料包不可以分片( Don’t Fragment , DF=1 ),路由器將 IP 資料包丟棄,併傳送 ICMP 分片錯誤訊息給源傳送端。
關於 MTU 的探測,參考《 Path MTU discovery 》。我們 可以通過基於 ICMP 協議的 ping 命令來探測從本機出發到目標機器上路由上的 MTU ,詳見下文。
TCP 和 UDP
在基於傳輸層( TCP/UDP )的應用開發中,為了最後的程式優化,應避免端到端的任何一個節點上出現 IP 分片。 TCP 的 MSS 協商機制加上序列號確認機制,基本上能夠保證資料的可靠傳輸。
UDP 協議在 IP 協議的基礎上,只增加了傳輸層的埠( Source Port+Destination Port )、 UDP 資料包長( Length = Header+Data )以及檢驗和( Checksum )。因此,基於 UDP 開發應用程式時,資料包需要結合 IP 分片情況考慮。對於以太區域網,往往取 UDP 資料包長 Length<=MTU-sizeof(IP Header)=1480 ,故 UDP 資料負載量小於或等於 1472 ( Length-UDP Header );對於公網, ipv4 最小 MTU 為 576 , UDP 資料負載量小於或等於 536 。
“ 向外” NAT 在內網和公網之間提供了一個“ 不對稱” 橋的對映。“ 向外” NAT 在預設情況下只允許向外的 session 穿越 NAT :從外向內的的資料包都會被丟棄掉,除非 NAT 裝置事先已經定義了這些從外向內的資料包是已存在的內網 session 的一部分。對於一方在 LAN ,一方在 WAN 的 UDP 通訊,鑑於 UDP 通訊不事先建立虛擬鏈路, NAT 後面的 LAN 通訊方需先傳送訊息給 WAN 通訊方以洞穿 NAT ,然後才可以進行雙向通訊,這即是常提到的 “UDP 打洞( Hole Punching ) ” 問題。
TCP 連線百度過程解析
下文對百度的完整抓包建立在不使用 快取的基礎上。如若主機存有百度站點的 cookie 和離線快取( Offline Cache ),則不會再請求位址列圖示 favicon.ico ;請求 /js/bdsug.js?v=1.0.3.0 可能回應 “HTTP/1.1 304 Not Modified” 。可在瀏覽器開啟百度首頁後,Ctrl+F5強制重新整理,不使用快取,也可參考《 瀏覽器清除快取方法 》。
以下為訪問百度過程, wireshark 抓包資料。對於直接通過 Ethernet 聯網的機器, Wireshark Capture Filter 為 host www.baidu.com ;對於通過 PPP over Ethernet ( PPPoE )聯網的機器, Wireshark Capture Filter 為 pppoes and host www.baidu.com 。以下抓包示例 直接通過 Ethernet 聯網訪問百度的過程。可點選圖片超連結下載pcap檔案,使用wireshark軟體檢視。
為方便起見,以下將客戶端(瀏覽器)簡稱為 C ,將伺服器(百度)簡稱為 S 。


1 . TCP 三次握手建立連線
“http://” 標識 WWW 訪問協議為 HTTP ,根據規則,只有底層協議建立連線之後才能進行更高層協議的連線。在瀏覽器位址列輸入地址後按下Enter鍵的瞬間, C 建立與 S (機器名為 www.baidu.com , DNS 解析出來的 IP 為 220.181.6.175 )的 TCP 80 連線( HTTP 預設使用 TCP 80 埠)。
以下為三次握手建立 TCP 連線的資料包( Packet1-Packet3 )。
1 192.168.89.125:5672 → 220.181.6.175:80 TCP( 協議 ) 62( 乙太網 幀長 )
amqp > http [SYN] Seq=0 Win=65535 Len=0 MSS=1460 SACK_PERM =1
2 220.181.6.175:80 → 192.168.89.125:5672 TCP 62
http > amqp [SYN, ACK] Seq=0 Ack=1 Win=8192 Len=0 MSS=1460 SACK_PERM=1
3 192.168.89.125:5672 → 220.181.6.175:80 TCP 54
amqp > http [ACK] Seq=1 Ack=1 Win=65535 Len=0
三次握手建立 TCP 連線的流程如下:
C(Browser) S(www.baidu.com)
1. CLOSED LISTEN
2. SYN-SENT → <SEQ=0><CTL=SYN> → SYN-RECEIVED
3. ESTABLISHED ← <SEQ=0><ACK=1><CTL=SYN,ACK> ← SYN-RECEIVED
4. ESTABLISHED → <SEQ=1><ACK=1><CTL=ACK> → ESTABLISHED
3-Way Handshake for Connection Synchronization
三次握手的 socket 層執行邏輯
S 呼叫 socket 的 listen 函式進入監聽狀態; C 呼叫 connect 函式連線 S : [SYN] , S 呼叫 accept 函式接受 C 的連線併發起與 C 方向上的連線: [SYN,ACK] 。 C 傳送 [ACK] 完成三次握手, connect 函式返回; S 收到 C 傳送的 [ACK] 後, accept 函式返回。
關於 Seq 和 Ack
Seq 即 Sequence Number , 為源端 ( source ) 的傳送序列號 ; Ack 即 Acknowledgment Number , 為目的端 ( destination ) 的接收確認序列號 。在 Wireshark Display Filter 中,可使用 tcp.seq 或 tcp.ack 過濾。
在 Packet1 中, C:5672 向 S:80 傳送 SYN 握手包, Seq=0(relative sequence number) ;在 Packet2 中 , S:80 向 C:5672 傳送 ACK 握手回應包, Ack=1(relative sequence number) ,同時傳送 SYN 握手包, Seq=0(relative sequence number) ;在 Packet3 中, C:5672 向 S:80 傳送 ACK 握手回應包, Seq=1 , Ack=1 。
至此, Seq=1 為 C 的 Initial Sequence Number ( ISN ),後期某一時刻的 Seq=ISN+ 累計傳送量 (cumulative sent) ; Ack=1 為 C 的 Initial Acknowledge Number ( IAN ),後期某一時刻的 Ack=IAN+ 累計接收量 (cumulative received) 。對於 S 而言, Seq 和 Ack 情同此理。
參考 :《TCP Analyze Sequence Numbers 》、《Understanding TCP Sequence and Acknowledgement Numbers 》
2 . TCP 獲取網站資料流程
連線建立後,下一步傳送( “GET / HTTP/1.1” )請求( Request ) HTML 頁面,這裡 “/” 表示 S 的預設首頁, “GET” 為 HTTP Request Method ; “/” 為 Request-URI ,這裡為相對地址; HTTP/1.1 表示使用的 HTTP 協議版本號為 1.1 。
以下為 HTTP GET 請求資料包( Packet4 )。
4 192.168.89.125:5672 → 220.181.6.175:80 HTTP 417
GET / HTTP/1.1
HTTP GET 報文長 =417-54=363 個位元組,其中 Next sequence number: 364(relative sequence number) 表示,若 在規定的時間內收到S 響應 Ack=364 ,表明該報文傳送成功,可以傳送下一個報文( Seq=364 );否則重傳(TCP Retransmitssion )。序列號確認機制是 TCP 可靠性傳輸的保障。
S ( http )收到 HTTP GET 報文(共 363 個位元組),向 C ( amqp )傳送 TCP 確認報文 ( Packet5 )。
5 220.181.6.175:80 → 192.168.89.125:5672 TCP 60
http > amqp [ACK] Seq=1 Ack=364 Win=6432 Len=0
這裡 Seq=1, 為 S 的 ISN ,意為已傳送過 SYN 。 Packet2 中, Ack=1 為 S 的 IAN 。這裡的 Ack-IAN=364-1=363 表示 S 已經從 C 接收到 363 個位元組,即 HTTP GET 報文。同時,Ack=364也是S期待C傳送的下一個TCP報文序列號(上面分析的 Next sequence number) 。
接下來, S 向 C 傳送 Http Response ,根據 HTTP 協議,先發響應頭( Response Header ),再發百度首頁 HTML 檔案。
Http Response Header 報文 ( Packet6 ) 如下 。
6 220.181.6.175:80 → 192.168.89.125:5672 TCP 465
[ TCP segment of a reassembled PDU ]
其部分內容如下:
======================================
HTTP/1.1 200 OK
……
Content-Length: 2139
Content-Type: text/html;charset=gb2312
Content-Encoding: gzip
======================================
S 響應 C 的 “GET / HTTP/1.1” 請求,先傳送帶 [PSH ] 標識的 411 個位元組的 Http Response Header ( Packet 6 )。
TCP 頭部 [PSH] 標識置位,敦促 C 將快取的資料推送給應用程式,即先處理 Http Response Header ,實際上是一種 “ 截流 ” 通知。相應 C 的 socket 呼叫 send 時 在 IPPROTO_TCP 選項級別設定 TCP_NODELAY 為 TRUE 禁用 Nagle 演算法可以 “ 保留髮送邊界 ” ,以防粘連。
儘管握手協商的 MSS 為 1460 ,但伺服器或者代理平衡伺服器,每次傳送過來的 TCP 資料最多隻有 1420 個位元組 。 可以使用 ping -f -l size target_name 命令向指定目標 target_name 傳送指定位元組量的 ICMP 報文,其中 -l size 指定傳送緩衝區的大小; -f 則表示在 IP 資料包中設定不分片( Don’t Fragment ),這樣便可探測出到目標路徑上的 MTU 。
執行“ ping -f -l 1452 www.baidu.com ”的結果如下:
220.181.6.18 的 Ping 統計資訊 :
資料包 : 已傳送 = 4 ,已接收 = 4 ,丟失 = 0 (0% 丟失 )
執行“ ping -f -l 1453 www.baidu.com ”的結果如下:
需要拆分資料包但是設定 DF 。
220.181.6.18 的 Ping 統計資訊 :
資料包 : 已傳送 = 4 ,已接收 = 0 ,丟失 = 4 (100% 丟失 )
從以上 ping 結果可知,在不分片時,從本機出發到百度的路由上能通過的最大資料量為 1452 ,由此推算出 MTU{local,baidu}=sizeof(IP Header)+ sizeof(ICMP Header)+sizeof(ICMP Data Portion)=20+8+1452=1480 。
S 呼叫 socket 的 send 函式傳送 2139 個位元組的 Http Response Content ( Packet 7 、 Packet 9 ),在 TCP 層將分解為兩段( segment )後再發出去。
7 220.181.6.175:80 → 192.168.89.125:5672 TCP 1474
[TCP segment of a reassembled PDU]
由 “Content-Length: 2139” 可知, HTML 檔案還有 2139-(1474-54)=719 個位元組。但此時, C 已經傳送了確認報文 ( Packet8 ) 。
8 192.168.89.125:5672 → 220.181.6.175:80 TCP 54
amqp > http [ACK] Seq=364 Ack=1832 Win=65535 Len=0
Seq-ISN=364-1=363 ,表示 C 已經發出了 363 個位元組,上邊已經收到了 S 的確認。 Ack-IAN=1832-1=(465-54)+(1474-54) ,表示 C 至此已經接收到 S 發來的 1831 個位元組。
接下來, C 收到 HTML 檔案剩餘的 719 個位元組,報文 ( Packet9 )如下。
9 220.181.6.175:80 → 192.168.89.125:5672 HTTP 773
HTTP/1.1 200 OK
至此, C 收到 S 傳送過來的全部 HTTP 響應報文,即百度首頁 HTML 內容 (text/html) 。
Packet6 、 Packet7 和 Packet9 的 ACK 都是 364 ,這是因為這三個segment都是針對 Packet4 的 TCP 響應。S將百度首頁HTML檔案(一個完整的HTTP報文)按照MSS分段提交給TCP層。 在 Wireshark 中可以看到 Packet9 的報文中有以下 reassemble 資訊:
[Reassembled TCP segments (2555 bytes): #6(411),#7(1420),#9(719)]
[Frame: 6, payload: 0-410(411 bytes)]
[Frame: 7, payload: 411-1830(1420 bytes)]
[Frame: 9, payload: 1831-2549(719 bytes)]
C ( amqp )接收到百度首頁的 HTML 檔案後,開始解析渲染。在解析過程中,發現頁面中含有百度的 logo 資源 baidu_logo.gif ,並且需要 bdsug.js 指令碼。
<img src=" http://www.baidu.com/img/baidu_logo.gif " width="270" height="129" usemap="#mp">
{d.write('<script src=http://www.baidu.com/js/bdsug.js?v=1.0.3.0><//script>')}
於是上面那個連線( C:5672 )繼續向 S 請求 logo 圖示資源,報文( Packet10 )如下。
10 192.168.89.125:5672 → 220.181.6.175:80 HTTP 492
GET /img/baidu_logo.gif HTTP/1.1
與此同時, C ( jms )新建一個連線( TCP 5 673 )向 S 請求 js 指令碼檔案。 報文( Packet11 )如下。
11 192.168.89.125:5673 → 220.181.6.175:80 TCP 62
jms > http [SYN] Seq=0 Win=65535 Len=0 MSS=1460 SACK_PERM=1
( Packet12 ) Packet13 、 Packet14 、 Packet16 和 Packet17 為對 Packet10 的 TCP 響應(它們的 Ack=802 ), 在邏輯上它們是一個完整的 TCP 報文。其 Http Response Content 為圖片檔案 baidu_logo.gif 。我們在 Wireshark 中可以看到 Packet17 的報文中有以下 reassemble 資訊:
[Reassembled TCP segments (1801 bytes): #13(312),#14(1420),#16(28) ,#17(41)]
[Frame: 13, payload: 0-311(312 bytes)]
[Frame: 14, payload: 312-1731(1420 bytes)]
[Frame: 16, payload: 1732-1759(28 bytes)]
[Frame: 17, payload: 1760-1800(41 bytes)]
Packet11-Packet19-Packet20 完成新連線的三次握手。然後, C ( jms )傳送 “ GET /js/bdsug.js?v=1.0.3.0 HTTP/1.1 ” 報文( Packet21 ),以獲取 bdsug.js 指令碼檔案。
21 192.168.89.125:5673 → 220.181.6.175:80 HTTP 465
GET /js/bdsug.js?v=1.0.3.0 HTTP/1.1
( Packet22 ) Packet23 、 Packet24 、 Packet26 和 Packet27 為對 Packet21 的 TCP 響應(它們的 Ack=412 ), 在邏輯上它們是一個完整的 TCP 報文。其 Http Response Content 為指令碼檔案 bdsug.js 。我們在 Wireshark 中可以看到 Packet27 的報文中有以下 reassemble 資訊:
[Reassembled TCP segments (3897 bytes): #23(310),#24(1420),#26(1420) ,#27(747)]
[Frame: 23, payload: 0-309(310 bytes)]
[Frame: 24, payload: 310-1729(1420 bytes)]
[Frame: 26, payload: 1730-3149(1420 bytes)]
[Frame: 27, payload: 3150-3896(747 bytes)]
通常,瀏覽器會自動的搜尋網站的根目錄,只要它發現了 favicon.ico 這個檔案,就把它下載下來作為網站位址列圖示。於是, C ( amqp )還將發起 “ GET /favicon.ico HTTP/1.1 ” 請求 網站位址列圖示,見報文 Packet29 。
3 . TCP 四次揮手關閉連線
經 Packet28 確認收到了完整的 japplication/javascript 檔案後,鏈路 1 (本地埠 5673 )使命結束, S 關閉該鏈路,進入四次揮手關閉雙向連線。
( Packet30 ) Packet31 和 Packet32 為對 Packet29 的 TCP 響應(它們的 Ack=1201 )。 經 Packet33 確認收到了完整的 image/x-icon 檔案後,鏈路 2 (本地埠 5672 )使命結束, S 關閉該鏈路,進入四次揮手關閉雙向連線。
為什麼握手是三次,而揮手是四次呢?這是因為握手時,伺服器往往在答應建立連線時,也建立與客戶端的連線,即所謂的雙向連線。所以,在 Packet2 中,伺服器將 ACK 和 SYN 打包發出。揮手,即關閉連線,往往只是表明揮手方不再傳送資料(無資料可發),而接收通道依然有效(依然可以接受資料)。當對方也揮手時,則表明對方也無資料可發了,此時雙向連線真正關閉。

參考:
《 瀏覽器 /網頁工作原理 》《 What really happens when you navigate to a URL 》
《 HTTP通訊過程分析 》
《 究竟什麼是 HTTP連線 》
《 一次完整的 HTTP通訊步驟 》
《 SOCKET與 TCP/IP與 HTTP的關係 》
《 TCP連線、 Http連線與 Socket連線 》

相關文章