前言
本篇是上篇《面試中的TCP/UDP協議》的姊妹篇,上篇簡單從概念的角度把TCP/UP協議的格式、特性解釋了一下,但並沒有在實際場景中看到TCP/UDP報文的格式,而走馬觀花的過一遍只是死記硬背,對於一些易混點還是一知半解,比如:
- TCP協議中的序號到底表示什麼意義?
- 在建立連接後,如果長時間沒有數據傳輸,TCP還會一直連接麼?
…
爲了感受實際場景中的TCP/UDP的格式,專門用wireshark來抓包, 實戰分析TCP、UDP的協議內容。
看完本文會瞭解到的知識點:
- TCP協議中的序號作用
- TCP連接中的KeepAlive
- HTTP協議中的KeepAlive
HTTPS抓包實例
這裏以我用瀏覽器訪問一次https://www.baidu.com爲例, 用wireshark進行抓包,重點看TCP協議在實際場景中的應用。
環境配置
- 192.168.219.136是虛擬機IP,用虛擬機中的瀏覽器訪問https://www.baidu.com
- 192.168.219.2是虛擬機默認網關
- 180.101.49.11是百度的ip地址
DNS數據包
wireshark的自動解析很不錯,每個標誌位的意義數值都標出來了。
注意:
- 正如我們在理論篇裏看到的, UDP協議僅僅規定端口號,以及數據的校驗。
- 瀏覽器首先通過DNS協議,向默認網關發起查詢請求,查詢www.baidu.com的ip地址
注意:
- 這裏有趣的一點是返回了三個查詢記錄,一個CNAME,兩個IP地址
- 經訪問,兩個IP地址均指向百度的首頁(多IP配置可能是爲了負載均衡),而www.a.shifen.com指向百度錯誤頁
域名中的A記錄和CName記錄
爲了更好的理解這幾個返回結果的關係,先了解下A記錄和CNAME記錄
A記錄是用來指定主機名(或域名)對應的IP地址記錄。
CNAME記錄用作別名,相當於域名之間的映射(例如www.baidu.com 映射到www.a.shifen.com),而不直接與IP地址對應。
在訪問www.baidu.com的時候,dns協議從dns服務器進行解析,查詢到www.baidu.com到www.a.shifen.com的映射記錄,而與真正IP地址(180.101.49.11, 180.101.49.11)綁定的則是www.a.shifen.com這個A記錄。
(有趣吧,經常訪問的www.baidu.com居然只是一個別名)
但問題來了,當我們在瀏覽器中直接輸入www.a.shifen.com的時候,並不會出來百度的頁面,而是直接倒到百度的錯誤頁面。
猜想可能是,在發起HTTP請求的時候,輸入的url會寫入HTTP請求頭部,這樣在服務端會進行判斷,發現不是www.baidu.com的時候,會顯示錯誤頁。
至於現在百度爲什麼還要保留這個www.a.shifen.com的DNS配置,就不得而知了,估計又是一個歷史遺留問題。
TCP三次握手
第一次握手:
注意:
- 我們在訪問的時候,應用層協議是https,所以目的端口號是443
第二次握手:
注意:
- 這裏我們要特意留心一下序列號的變化,由於seq是隨機產生的,wireshark爲了便於分析,在解析的時候,使用相對序列號(Relative Sequence number)進行顯示,所謂“相對”,即是把第一個TCP包(第一次握手)的序列號置爲0,接下來的序列號依次增長。
- 這裏的Acknowledgment number指的是期望下次收到包的序列號(在ACK爲置爲1時有效),以下簡寫爲Ack(與全大寫標誌位ACK區分),
因爲TCP包是以流水線形式發出的,比如發送端順序的發出 Seq=1、Seq=2、Seq=3。 那麼如果ACK確認的序號和收到的包的序號一致的話,那麼需要發回 Ack=1、Ack=2、Ack=3 共三個包。
但是TCP協議對此進行了優化,只需要發送一個ACK包就能代表說自己已經收到了前面三個包, 那就是發送Ack=4 (期望收到Seq爲4的包)。這樣節省了ACK確認的數量。)
第三次握手:
注意:
- 這裏有意思的是客戶端根據服務端發來的窗口值,對自己的窗口值進行了調整,便於接受發來的數據。
其實TCP協議中比較核心的還是序列號的變化,光用三次握手體現不出這個關係,下面對多個數據包的數據流圖來對序列號的變化進行分析。
TCP協議流圖
wireshark可以繪製TCP流,放這個圖主要是想理清一下包序號的之間的關係。
前6個包序號變化
- 第一次握手: Seq = 0,客戶端發出
- 第二次握手: Seq = 0, 服務端發出,Ack=1,表示想接收到下次客戶端序號爲1的包
- 第三次握手:Seq = 1, (確認包,不帶數據),客戶端發出,Ack=1, 表示想接收到下次服務端序號爲1的包
- 第4個包: Seq = 1, 客戶端開始向服務端發送數據(長度爲153),注意!這裏的序號並沒有變爲2,還是1,並且Ack還是1
- 第5個包: Seq = 1,(確認包,不帶數據),注意!這裏的序號並沒有變爲2,還是1, Ack=154,表示已收到長度爲153的數據,期待下次收到序號爲154的包
- 第6個包: Seq = 1,服務端開始向客戶端發送數據(長度爲138),注意!這裏的序號並沒有變爲2,還是1, Ack=154,表示已收到長度爲153的數據,期待下次收到的154的數據
…
總結
從上面這6個包的接收發送,我們可以總結出以下幾點:
-
TCP通信的一方,在接收到數據包後,可能會發出(1. ACK確認包(不帶數據), 2. ACK確認包(帶數據))或者只發出ACK確認包(帶數據),需要明確的一點是,在第一種情況中,兩個包的序號並沒有改變(並沒有因爲是兩個包,第二個包序號就比第一個包序號多一,這有點反直覺)。
說明了序號的增長,並不是依賴自己發包的數量和順序,而是依賴對方已接收並確認的數據
-
TCP序號根據數據流編碼,Ack=154不僅僅表示期待收到對方序列號爲4的包,另一層意思是,確認已經收到153字節,並期待下次收到第154字節起始的數據(雖然這兩種理解產生的序號值都一樣)
TCP KeepAlive
這裏對上面總結中提到發出兩個序列號一樣的數據包做出進一步的解釋:
仔細回看一下上面的TCP流圖,我們會發現一方會向另一方發送ACK包(不帶數據),而且序號與接下來要發送的數據包一致,這種數據包一方面是對已收到消息的回覆,另外一種含義則是爲了刺探通信的對方是否存活。 原因如下:
長連接的環境下,進行一次數據交互後,很長一段時間內無數據交互時,客戶端可能意外斷電、死機、崩潰、重啓,還是中間路由網絡無故斷開,這些TCP連接並未來得及正常釋放,那麼,連接的另一方並不知道對端的情況,它會一直維護這個連接,長時間的積累會導致非常多的半打開連接,造成端系統資源的消耗和浪費,且有可能導致在一個無效的數據鏈路層面發送業務數據,結果就是發送失敗。所以服務器端要做到快速感知失敗,減少無效鏈接操作,這就有了TCP的Keepalive(保活探測)機制。 ------理解TCP長連接(Keepalive)
與下文中HTTP的KeepAlive的區別
HTTP的KeepAlive是爲了保證TCP連接的連接複用
而TCP的KeepAlive是爲了保證TCP連接的可用
這裏比較意外的是沒有抓到FIN包
猜測HTTP使用長連接,這也符合我們常規的邏輯(進入一個網站,我們通常還會獲取這個網站的其他數據,如果頻繁的斷開,連接,會導致不小的時間開銷。)
以下是查詢資料得到的答案:
HTTP1.1規定了默認保持長連接(HTTP persistent connection ,也有翻譯爲持久連接),數據傳輸完成了保持TCP連接不斷開(不發RST包、不四次握手),等待在同域名下繼續用這個通道傳輸數據。
HTTP首部的Connection: Keep-alive是HTTP1.0瀏覽器和服務器的實驗性擴展,當前的HTTP1.1 RFC2616文檔沒有對它做說明,因爲它所需要的功能已經默認開啓,無須帶着它,但是實踐中可以發現,瀏覽器的報文請求都會帶上它。
如果HTTP1.1版本的HTTP請求報文不希望使用長連接,則要在HTTP請求報文首部加上Connection: close。
此次時間有限,下次實驗補上這部分(修改HTTP報文中的Connection,看能否抓到FIN包)。
後記
雖然wireshark把很多數據含義都進行了標識,但是其中的一些內在聯繫還是需要我們自己去挖掘(如序列號關係、DNS解析流程),而比較重要的東西在課本上要麼是一筆帶過,要麼缺少實例分析,對細節提及甚少。所以學習還是還要自己主動a …