久久综合九色综合97婷婷-美女视频黄频a免费-精品日本一区二区三区在线观看-日韩中文无码有码免费视频-亚洲中文字幕无码专区-扒开双腿疯狂进出爽爽爽动态照片-国产乱理伦片在线观看夜-高清极品美女毛茸茸-欧美寡妇性猛交XXX-国产亚洲精品99在线播放-日韩美女毛片又爽又大毛片,99久久久无码国产精品9,国产成a人片在线观看视频下载,欧美疯狂xxxx吞精视频

有趣生活

當前位置:首頁>職場>面試tcp ip問什么(面試需要知道的)

面試tcp ip問什么(面試需要知道的)

發布時間:2024-01-24閱讀(10)

導讀TCP是一套相當復雜的協議,包含的內容也非常多,面試也非常常見,不少新手面試這種面試,一頭霧水,不知道如何下手,也不知道從何看起,拿起TCP/IP詳解,也找....

TCP 是一套相當復雜的協議,包含的內容也非常多,面試也非常常見,不少新手面試這種面試,一頭霧水,不知道如何下手,也不知道從何看起,拿起 TCP/IP 詳解,也找不著重點,看兩頁就犯困。

為了解決大家的困惑,花了兩天的時間,幫大家梳理一下,作為一名開發者,應該需要重點掌握哪些 TCP 知識。當然,由于只有一篇文章,不可能面面俱到,否則就要爆炸了!因此,我挑了重點中的重點來介紹。

關于參考書:

  • TCP/IP 詳解卷一(作者:W.Richard Stevens)
  • TCP/IP 協議簇(作者:Behrouz A.Forouzan)

是的,本文幾乎所有內容在上面的兩本書你都能找到(除了實驗之外),我當然不是抄書,那樣有什么意義呢?本文的目的是梳理,就像老師劃的重點一樣,你才有復習的方向,特別適合那些時間緊迫,但是又來不及完整去看一遍書的同學。

提示:

  • 在學習之前,請確保自己有 >2 小時的連續空閑時間,走馬觀花,很難消化哦!
  • 準備一臺 Linux 服務器,如果沒有云主機,那就自己安裝一個 Linux 虛擬機來做服務器吧。
  • 準備好本地 Linux 客戶機,虛擬機創建一個就行。

接下來,我們拿起大刀—— tcpdump,開始 TCP 之旅吧。

1. 三次握手和四次揮手

這里打算使用借助三次握手和四次揮手,來熟悉一下 tcpdump 工具,后面你會多次使用這個工具,沒用過的同學一定要認真看,工作的時候你也會用上它的,尤其是在分析網絡數據傳輸異常的時候!

面試tcp ip問什么(面試需要知道的)(1)

圖1 三次握手與四次揮手

上圖是我使用 nc 命令配合 tcpdump 抓包工具演示的一次三次握手與四次揮手的過程。這個實驗非常簡單,只要你有搭載 Linux 或者 MacOS 的主機,就可以輕松的進行這個實驗。實驗步驟如下:

  • Step.1 打開三個終端。
  • Step.2 下面窗口輸入 sudo tcpdump -# -S -n -i lo0 tcp and host 127.0.0.1 and port 8000 啟動抓包程序。
  • Step.3 左上窗口輸入 nc -l 8000 表示在端口 8000 啟動一個 TCP 服務程序。
  • Step.4 右上窗口輸入 nc localhost 8000 表示向 localhost:8000 這個地址發起 TCP 連接請求。

不出意外,你就能在下方的窗口里看到抓取到的三次握手的報文了。接下來,你順便可以觀察一下四次揮手的過程。步驟如下:

  • Step.5 右上窗口按下 CTRL C 組合鍵,退出客戶端程序。

一旦退出客戶端,你就能看到四次揮手的過程了。如果你在阿里云或者騰訊云有服務器的話,那就更好了,你可以在你的服務器上使用 nc 命令啟動一個 TCP 服務器,在本地使用 nc 命令連接,這樣更加真實哦!

好,咱們簡單分析一下 tcpdump 命令的參數含義,以及報文的含義。

1.1 tcpdump

tcpdump 是在類 Unix 環境下的抓包神器,在你的 Linux 或 MaxOS 系統上都是默認安裝好的,它可以非常方便的抓取網卡上的數據包,并且可以根據你指定的參數進行過濾。在上面的實驗中,各個參數含義如下:

面試tcp ip問什么(面試需要知道的)(2)

更多的參數,你可以使用 man tcpdump來查閱文檔,它的文檔非常詳盡,你可以找到關于 tcpdump 的一切。另外,MacOS 和 Linux 上的 tcpdump 有一點區別,但是這些影響都不大。

1.2 報文含義

結合圖 1,分析一下每一包的含義。注意,這里使用 ACK 表示標志位,使用小寫 ack 表示序號。 另外,C 表示客戶端,S 表示服務器(就是使用 nc -l 8000 的那個)。

面試tcp ip問什么(面試需要知道的)(3)

上表是圖 1 中的報文簡化后的情況,這里提取了一些關鍵數據。

  • 在 tcpdump 中,標志位都放在 [] 中,比如 [S.],其中 S 就表示 SYN,F 表示 FIN,而 . 號表示 ACK。
  • 可以看出,除了第一包握手報文外,其它所有報文都帶有 ACK 標志。
  • 帶有 ACK 標志的報文,表示收到了 ack-1 號報文,并且接下來期望收到對方序號為 ack 的報文。
  • 第 4 包是一個重傳報文,很容易發現,這一包啥也沒干,就是把 ACK 重傳了一遍。
  • 由于在實驗里,我們先退出的客戶端,因此是客戶端先斷開連接,因此客戶端發送 FIN 到服務器端(對應的第 5 包)。

關于三次握手,待會在第 5 節,還有更重要的內容!這里只是讓你先適應一下 tcpdump 工具,以及放松下心情。

2. Delay ACK

接下來的事情就好玩了,這是一個非常重要且鮮有人提起的東西,稱之為 Delay ACK(延時確認)。話說,它到底是個什么玩意兒?很重要嗎?廢話不多說,先來做個實驗。

面試tcp ip問什么(面試需要知道的)(4)

圖2 Delay ACK

來看一個發生在互聯網上的例子,這次我的服務器位于騰訊云主機上。建立 TCP 連接后,我從客戶端發送了 4 次數據到一個名為 mars 的服務器上,第一次發送一個字母 a 再加一個字節的回車 ,第二次發送了一個字母 b 加回車 ,后面還有 c, d 同理。

echo.go(點我下載) 是我用 golang 寫的一個簡單的 TCP 服務器,默認情況下,這個服務器什么也不干,只管收數據,就像第 2 節里使用的 nc 命令一樣,不過 echo.go 收到數據直接丟棄了,甚至也不顯示在屏幕上。

如果你還沒有安裝 golang 編譯器,我也為你編譯好了一個 echo,就放在代碼庫里,你可以直接運行。不過還是強烈建議你自己安裝一下 golang,安裝方法,請參考文末,沒把鏈接貼在這里,是因為我希望你不要現在就去嘗試安裝,等你看完文章再去做這件事。

這次在抓包程序 tcpdump 運行在服務器上,因為我想觀察服務器端是如何回復 ACK 報文段

這次就不再分析三次握手和四次揮手了,從圖 2 上看太簡單了,一目了然是吧。重點放在服務器接收 4 次數據的行為,正好對應 TCP 的 4、5、6、7、8、9、10、11 號報文。同樣這里我們用表格記錄一些關鍵信息。

面試tcp ip問什么(面試需要知道的)(5)

一些觀察到的現象:

  • 客戶端發送 a(第 4 包)到服務器后,服務器立即返回了 ACK(第 5 包) 到客戶端,沒有經過任何等待。
  • 客戶端發送 b(第 6 包)到服務器后,服務器沒有立即返回 ACK,而是等待了約 40ms 才返回 ACK(第 7 包)到客戶端。
  • 第 8、9、10、11 包也是一樣。

這里你需要關注的問題是,服務器接收到數據后,為什么有時候沒有立即返回 ACK,而要等待 40ms 呢? 很好,我希望你能看到這個現象,這不是巧合,而是 TCP 的特性,是一種機制,它就是 Delay ACK,即延遲 ACK。

為什么我沒有使用 nc 繼續做服務器,因為在我的 Linux 系統上,這個機制默認是關閉的,如果使用 nc 命令,你可能看不到這種現象,因此我使用 golang 寫了一個簡單的 TCP 服務器,來開啟 Delay ACK 這個機制。當然,我希望你在你的服務器上使用 nc 工具嘗試實驗一下,也許能看到,也許看不到,具體取決于你機器的內核版本。

TCP 為什么要引入這種機制呢?目的是為了減少網絡中 TCP 報文段的數量。在過去,帶寬那可是相當的貴,其實現在也不便宜。你知道,一個 TCP 首部至少需要 20 字節(稍后會幫你梳理一下 TCP 首部字段),而引入了 Delay ACK,就可以做兩件事:

  • 累積確認:服務器極有可能在這 40ms 里又收到了客戶端發送過來的多個 TCP 報文段,40ms 后就可以對這些報文進行累積確認,也就是只返回一個 ACK 報文就行。這樣就能減少網絡中 ACK 報文的數量。
  • 捎帶確認:40ms 里,服務器也可能會返回數據給客戶端,如果服務器有數據返回給客戶端,那不如把這個 ACK 連同數據一起返回給客戶端吧,等一下下是值得的。就好比你要出門和朋友吃飯,但是你女朋友可能也想和你一起去,然而你女朋友要化妝什么的,速度非常慢,于是你想了個策略,你等她 30 分鐘,如果 30 分鐘內她搞定了,你就帶她一起去,如果她搞不定,你就不帶她去了。(什么?程序員不可能有女朋友,其實男朋友也可以的^_^)。

好,接下來再談談 echo 是怎么把 Delay ACK 機制打開的。非常簡單,你使用 man 手冊查閱 man 7 tcp,就能看到一些關于 TCP 機制的文檔,其中有一項是 TCPQUICKACK。

面試tcp ip問什么(面試需要知道的)(6)

圖3 TCPQUICKACK

不過這個選項的名字和 Delay ACK 的含義是相反的。這意味著,如果你想開啟 Delay ACK,你就得把這個選項設置成 false。Linux 提供了 setsockopt 系統調用來幫你設置,關于這個函數,你可以使用 man setsockopt 來查閱。

int opt = 0;setsockopt(sockfd, IPPROTO_TCP, TCP_QUICKACK, &opt, sizeof(int));

另外,你需要在每次 recv 數據后,都需要調用一下這個設置函數,因為在 man 手冊中有明確說明,這個選項設置并不是永久的。

在有些低版本的內核里,Delay ACK 機制默認就是開啟的哦!不過具體還需要你自己進行實驗。

3. Nagle

Nagle 也是 TCP 協議中常見的算法,而且面試也會經常問。Nagle 算法的目的,也是為了減少網絡中 TCP 報文段的數量。(你看,為了減少網絡中報文段的數量,TCP 協議搞了很多機制,包括上一節學習的 Delay ACK。)

Nagle 算法的發明者 John Nagle 當時發明了這個算法,主要是解決福特汽車公司的網絡擁塞問題。

Nagle 算法原理相當簡單:

  • 一個 TCP 連接上最多只能有一個未被確認的未完成的小分組,在它到達目的地前,不能發送其它分組。
  • 在上一個小分組未到達目的地前,即還未收到它的 ACK 前,TCP 會收集后來的小分組。當上一個小分組的 ack 收到后,TCP 就將收集的小分組合并成一個大分組發送出去。

上面的分組說的就是 TCP 報文段,一個意思。不過有一點值得注意,Nagle 算法關心的是小分組,也就是大分組它并不管。分組要多小才算是小呢?一個字節?兩個字節?一般來說,只要數據量小于 MSS,就是小分組。MSS 在大小在三次握手的時候就協商好了,不信你回去看看圖 1 或者圖 2(雖然圖 1 中的 MSS 大的有點過分,畢竟是環回網卡上的報文)。

為了驗證 Nagle 算法的確是存在的,再來個實驗吧。

3.1 實驗一(觀察 Nagle 算法的存在)

這個實驗看起來沒那么容易做,如何在極短的時間里發送多個小分組呢?繼續使用 nc 命令可以嗎?第一次發送 a,第二次再輸入一個 b回車發送,第三次輸入c 回車發送出去。很遺憾,哪怕你單身 30 年,你的手速也不可能突破到 100ms 以內,還沒等你 b 輸入進來,a 的 ACK 就已經收到了,所以這樣實驗的話,你永遠看不到 Nagle 是如何合并小分組這個過程。

既然如此,手速不夠快,C/C 寫起來太費事,咱們直接用 Python,方便快捷學習網絡編程的神器。

  • 開三個窗口,一個抓包,一個服務器,還有一個客戶端,寫 Python 腳本。
  • Python 只要寫 4 行就行了,重點在于使用 for 循環連續調用 5 次 send 的過程。

圖 4 是實驗的結果。具體代碼我就不貼進來,你自己敲一遍,記得更加清楚。

面試tcp ip問什么(面試需要知道的)(7)

圖4 Nagle 算法觀察

3.2 結果分析

簡單看一下圖 4 的結果。

  • 第 4 包是第一次調用 send 發送的數據,只有一個字節的 a,中間經過了約 30ms 后,收到了第 5 包,也就是 ACK 報文。
  • 第 6 包是后 for 循環的后 4 次合并的數據,一共是 4 個字節,即 aaaa,一次全部發送出去了。所以可以看出來,Nagle 算法默認就是開啟的。后面我們要想辦法把它關閉。

實驗非常簡單,非常容易就驗證了 Nagle 算法它的確是存在的,是不是很開心?但是在服務器開發中,通常我們都希望關閉 Nagle 算法,因為在互聯網技術如此發達的現在,網速已經足夠快了,也不那么擁堵,開啟 Nagle 反而會影響程序的響應速度。

如果這時候對方再開啟 Delay ACK 機制的情況下,發送方收到 ACK 的時間會拖慢 40ms(Delay ACK 簡直就是豬隊友),這在某些場景下幾乎是無法接受的。想想你玩王者榮耀的時候,那可是毫秒必爭啊,從 60ms 變成 100ms 那可能就是個人頭的問題。

3.3 實驗二(關閉 Nagle)

實驗二自然就是關閉 Nagle 算法啦,非常簡單,只要設置 TCP_NODELAY 選項就可以了,繼續在剛剛的 Python 終端里鍵入命令 s.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1),然后再連續發送 5 次報文。

面試tcp ip問什么(面試需要知道的)(8)

圖5 關閉 Nagle

從上面的結果看到,有了十分明顯的變化,第 8 到 12 包顯然直接從客戶端發送出去,沒有合并報文。整體的分組數量(8~17號報文)相比開啟 Nagle(4~7號報文)多了 6 包出來,如果忽略掉那 1 字節的數據,只看 TCP 首部的 20 字節,也就是多了 120 字節的在網絡中。

從這個角度來看 Nagle 算法,可以發現 Nagle 對網絡十分友好,而關閉 Nagle 可能會讓網絡造成擁塞(網絡中充斥著小分組)。

3.4 Nagle 算法的影響

我們來看一個實際場景(這個例子來源于《Unix 網絡編程》一書)。假設你基于 TCP 協議設計了一個應用層協議,這個協議一共有 400 字節的數據,其中首 4 個字節是消息的類型,剩余的 396 字節是數據。

如果客戶端分兩次發送數據,第一次發送 4 字節的數據類型,第二次再發送 396 字節的數據,也就是連續調用兩次 send 函數。服務器收到這個 4 字節卻什么都不能做,只能繼續等待剩余的 396 字節。假設網絡延時(RTT)是 1ms,另外服務器如果開啟了 Delay ACK 機制,同時客戶端開啟了 Nagle 算法,那將會是這樣一種情形:

  • 客戶端發送 4 字節出去,經過 41ms 后(服務器的 Delay ACK 40ms 網絡延時 1ms),收到了服務器返回的 ACK。
  • 客戶端繼續發送剩余的 396 字節。

這中間幾乎有 40ms 的時間幾乎浪費在了 Delay ACK 上,這簡直不能忍啊!如果關閉 Nagle 算法,程序性能會有極大的提升,因為不必等待服務器的 ACK 返回,剩下的 396 字節就能直接發送出去。

不過《Unix 網絡編程》一書(7.9節 TCP Socket Options)提到,關閉 Nagle 并不是最好的方案,作者認為這是一種有損于網絡的做法,正確的做法是應用層自己把報文合并,一次調用 write 函數。我認為作者說的完全沒有問題,但是這個語境我覺得放在網絡延時較長的鏈路中更加合適(比如 RTT 在 60ms 以上)。通常在局域網中,網絡延時幾乎可以忽略,基本上都會選擇關閉 Nagle,這主要是防止 Delay ACK 這個豬隊友讓你的網絡程序性能變差。

而服務器端,最好也應該要開啟 TCP_QUICKACK 選項(如今的 Linux 默認已經開啟),關閉 Delay ACK 機制。

4. 流量控制與擁塞控制

TCP 協議的面試中,你經常會看到這兩個名詞,面試官基本上都會考察一下你是否對 TCP 真的熟悉,就會簡單問一下,說說 TCP 流量控制算法是做什么的,擁塞控制呢?

其中有一個算法,直接體現在了 TCP 首部字段中,它就是流量控制算法,對應的字段是窗口大小。很多人分不表這兩個算法之間的異同點。下面簡單總結一下:

  • 相同點
  • 它們都是為了控制發送數據量的大小。
  • 不同點
  • 流量控制,是根據接收者能力情況,來控制發送數據量。
  • 擁塞控制,是根據網絡的擁塞狀態,來控制發送數據量。

最后實際要發送的數據量,取決于流量控制和擁塞控制計算出來的發送數據量的較小者。通常用 rwnd 來表示接收方的接收能力,用 cwnd 表示鏈路還能承載的數據能力,最終要發送的數據量是:

面試tcp ip問什么(面試需要知道的)(9)

在流量控制算法中,對端的接收能力是指對端還能接收多少數據,這個數值體現在對方發送給你的 TCP 報文的首部字段 winsize 中。比如對方說,它只能接收 100 個字節,那你就只能再給它發送不超過 100 字節的數據。如果你要發送的數據超過了 100 字節,抱歉,除去 100 字節剩下的部分就只能先暫存在本地的發送緩沖區中。

關于流量控制的經典算法,就是滑動窗口算法了,限于篇幅這里就不具體介紹了,你可以參考這篇文章《滑動窗口算法》。我們需要把更多的時間放在擁塞控制算法上。

4.1 擁塞控制算法

擁塞控制算法的目的,就是為了防止網絡在擁塞的情況下,還在瘋狂的向網絡中發送大量數據。那么這里就有一個值得關心的問題:發送者,如何知道網絡擁塞?

4.1.2 慢啟動

在建立完連接后,發送方有辦法知道網絡的擁堵情況嗎?顯然不能,那怎樣才能知道?想必你能猜出來,沒錯,只能試探。TCP 采取的策略就是試探,而且把這種方法取名為慢啟動

如果在發送過程中,遇到了重復 ACK 或者超時的情況,需要減慢發送速度:

  • 連續收到 3 次對方重復的 ACK 確認
  • 這意味著對方極有可能沒有收到數據,幾乎可以認為丟包了。但這并不代表網絡擁塞,甚至網絡狀況還不錯呢。(稍后解釋。)
  • 如果超時未收到 ACK,說明極有可能擁塞
  • 對方可能沒收到報文
  • 對方收到報文,但 ACK 丟了

一定要嚴格區分,重復 ACK超時這兩種情況,它影響了 TCP 擁塞算法做何種決策!!!

面試tcp ip問什么(面試需要知道的)(10)

圖6 慢啟動(一)

面試tcp ip問什么(面試需要知道的)(11)

圖7 慢啟動(二)

圖 6 和圖 7 是我抓取的到一段報文,可以看到一開始客戶端發送的速度并不算很快,一次發送兩個報文,經過一段時間后,就變成一次發送 10 多個報文。不過從抓取的數據包上看,并未出現丟包的情況,網絡狀況非常好。你也可以自己找一臺機器進行實驗,實驗過程非常簡單,寫 4 行 Python 語句即可。

如果 TCP 在發送中途,遇到丟包或超時情況,那就必須用減慢發送速度,一次少發一些報文段。比如一次發送 16 個報文段時,出現了異常(三次重復 ACK 或超時),那下次發送的時候數量減半,一次發送 8 個。

關于慢啟動的實驗,這篇《慢啟動》做的實驗更加清晰且容易觀察,你可以參考一下。

4.1.3 慢啟動算法

最經典,最原始的慢啟動算法是這樣的:

在程序中,維護一個變量 cwnd,表示擁塞窗口大小,單位是字節。在最開始,cwnd 有一個初始值,RFC 2581 規定,它的大小不超過 2MSS。為了方便以后的描述,當我說 cwnd = 2 時,實際上是說 cwnd = 2MSS,后面的 MSS 就省略掉。(MSS 在后面會解釋,表示最大報文段長度,一般在 1400 字節左右。)

為了方便描述這個算法,不妨約定 cwnd 初始值為 1(實際大多你看到的是 2)。

  • 首先發送方發送一個 cwnd = 1 的報文。
  • 發送方每收到一個確認,就把 cwnd 值加 1。

具體可以看圖 8 的時序圖。

面試tcp ip問什么(面試需要知道的)(12)

圖8 慢啟動

4.1.4 擁塞避免算法

為了防止慢啟動過程中 cwnd 增長的過大,TCP 中還維護了另一個變量 ssthresh,單位為字節。它稱之為慢啟動門限,這是一個閾值,當 cwnd 超過這個值的時候,慢啟動算法結束,進入擁塞避免算法!

這時候,TCP 發送 cwnd 個報文后,如果接收到了所有確認報文,cwnd 的值總和只是加 1,而不是加倍(也就是每收到一個確認報文,cwnd 加 1/cwnd)。這樣,擁塞窗口 cwnd 就會按線性規律緩慢增長。

有文獻將這個過程稱為 “加法增大”

面試tcp ip問什么(面試需要知道的)(13)

圖9 慢啟動(中間出現超時)

4.1.5 擁塞檢測過程

無論是在慢啟動階段,還是在擁塞避免階段,只要發送方判斷網絡可能出現擁塞(依據就是沒有按時收到確認,或者收到三次重復的 ACK),就要把 ssthresh 設置為出現擁塞時的 cwnd 值的一半)。

對于超時和收到三次重復 ACK,需要分別進行考慮,這兩者之間是有區別的,而且需要嚴格區分。

a. 超時(圖9)

如果計時器超時,出現擁塞的可能性就非常大(連重復的 ACK 都收不到),此時 TCP 反應強烈

  • 這時候把 ssthread設置為當前 cwnd 的值的一半.
  • cwnd 值再設置成 1,
  • 接下來重新從慢啟動開始。

這樣做的目的是要迅速減少主機發送到網絡中的分組數,使得發生擁塞的中間設備有足夠的時間把緩沖區中積壓的分組處理完畢。參考圖 9。

b. 連續收到三次重復的 ACK(圖10)

初步可以判定網絡沒有擁塞,只是大概率丟失了一個報文。為什么能判定為沒有擁塞呢?因為對方在收到失序報文的時候,就會立即返回一個 ACK(這種情況不受 Delay ACK 機制的影響,注意,是立即返回。)既然對方能一連串返回三個重復的 ACK,說明對方應該是連續收到三次的失序報文。你都能連續收到三次失序報文了,說明網絡并不差。

失序報文:比如,接收方期望接收 100 號報文,但卻收到了其它序號的報文(沒有按照應該有的順序收到)。

這個時候,發送方收到了三次重復 ACK,應該立即重傳丟失的報文,而不是等待重傳計時器超時。這個策略被稱為快重傳

發生快重傳的時候,雖然網絡可能沒有擁塞,但是也要降低數據發送速率,只是 TCP 反應較弱,執行快恢復算法

  • 這時候把 ssthread設置為當前 cwnd 的值的一半。
  • cwnd 值設置成 ssthread(也有些實現設置成 ssthread 3)。
  • 進入擁塞避免階段。

面試tcp ip問什么(面試需要知道的)(14)

圖10 快恢復算法

下面是某個小伙伴制作的一個擁塞控制算法快恢復算法的流量曲線,哈哈。

面試tcp ip問什么(面試需要知道的)(15)

5. 三次握手是必須的嗎?

我們從另一個角度來分析這個問題。假如使用 UDP 協議來實現可靠傳輸,應該怎么做呢?

最重要的是得解決可靠性問題,那可靠傳輸如何保證?

  • 超時重傳
  • 確認機制

只要具備這兩個機制,我們就能實現最基本、最簡單的可靠傳輸。要想支持這兩點,那么你所發送的所有報文都需要進行編號。接收方按照編號的順序,重組報文,最終應用層就可以讀取到有序的數據。

那么問題來了,接收方如何知道哪一包是第一包的報文呢?不妨約定第一包的編號為 0,如此我們就可以快樂的使用UDP 確認機制 超時重傳來保證可靠的數據傳輸了。

這樣看起來,我們也沒有進行三次握手呢!照樣可以實現可靠傳輸,那 TCP 的三次握手的意義在哪里呢?敲黑板了!!!

  • 協商第一包數據編號。這個第一包編號,稱為 ISN (Initial Sequence Number,初始序號),一定要記住這個名詞,很重要,默念幾遍。剛剛我們約定的策略是,第一包數據是編號是 0,那黑客就可以利用這個漏洞,偽造 TCP 報文,篡改數據,有了 ISN 后,雙方就可以通過 ISN 來協議第一包數據編號了。通常實現上,ISN 的初始值是一個隨機數(看你怎么猜!)。
  • 協商 MSS 以及確認雙方數據接收能力。

通俗的說,三次握手的目的是要建立一個信任關系,探測對方的老底,確認對方的能力,同時防止被黑客攻擊。

6. MSS 與 MTU

MSS 全稱為 Maximum Segment Size,即最大報文段長度。在前面也不止一次提到它,在第 7 節也提到三次握手其中一個目的也是為了協商 MSS 的大小。

一般來說,TCP 報文段攜帶的數據當然是越多越好。

如果 TCP 報文段傳輸的數據只有一個字節,在 IP 層傳輸的數據報大小就是 40 1 = 41 字節(至少 20 字節的 IP 頭 20 字節的 TCP 頭 1 字節數據)。這樣網絡的利用率就只有 1/41. 傳輸 nn 字節的數據利用率就是 n/(40 n)n/(40 n),顯然 TCP 報文段傳輸的數據如果越大,網絡利用率就越高。

但是實際上并非如此。因為網絡傳輸數據時,數據是最終是要交付到鏈路層協議上的,也就是說最后要封裝成“幀”。二型以太網(Ethernet Type 2)中規定,幀的大小不能超過 1518 個字節(14 字節的幀頭 4 字節幀校驗和 最多 1500 字節數據)。所以 IP 數據報的大小如果超過了 1500 字節,要想交付給鏈路層就必須進行“分片”。

“分片”指的是一個 IP 數據報太大,需要拆分成一個一個的小段,變成多個 IP 數據報。這種分片顯然是不利的,有一定的開銷。為了避免分片開銷,我們希望 IP 數據報的大小不超過 1500 字節。除去 IP 數據報的首部 20 字節,也就是希望 TCP 報文段不超過 1480 字節。再減去 TCP 報文段首部 20 字節,也就是 TCP 攜帶的數據不超過 1460 字節。

實際上,鏈路層對這種幀數據長度的限制稱為最大傳輸單元(Maximum Transmission Unit, MTU)。不同的鏈路層協議,對 MTU 的值也有所規定,通常這個值可以進行更改。使用命令 netstat -i 可以看到自己的網卡的 MTU 值。

面試tcp ip問什么(面試需要知道的)(16)

圖11 查看網卡的 MTU

有些同學對 MSS 與 MTU 的概念仍然很模糊,分不清兩者之間的關系。

  • MSS 是軟件層的概念,它是由軟件控制的
  • MTU 是硬件(比如網卡出口)的屬性,是指二層鏈路層幀攜帶的數據最大大小。

再舉個例子,MTU 的大小就好像一座橋的承重噸位,而橋就相當于網卡。事先給定 MSS,可以防止因為你貨車載貨過多,要進行分批運輸。如果不指定 MSS,一旦你貨車超載,噸位超過橋的承重能力,你就得把貨拆分成幾批運過去,運過去之后你還得組裝,這是得不償失的。

7. TIME_WAIT

面試tcp ip問什么(面試需要知道的)(17)

圖12 TIME_WAIT

TIMEWAIT 是 TCP 協議中的一種狀態,而且非常容易觀察到。上面的實驗利用 nc 命令發送數據,然后在客戶端執行 netstat -ant 命令,就能看到 TIMEWAIT 狀態了。

既然這個狀態很容易觀察到,說明 TCP 在這個狀態下一定是停留了相當長的時間。

TIMEWAIT 狀態,又稱為 2MSL 等待狀態。只有主動關閉一方才能進入 TIMEWAIT 狀態。MSL(Maximum Segment Lifetime)表示報文段最大生存時間,它表示任何報文段被丟棄前在網絡內的最長時間,實際上這個時間和 TTL 有關(TTL 是 IP 協議中的一個概念,表示能夠經歷的路由器的跳數,這個跳數是有限制的,最大值為 255)。

然而,MSL 卻不用跳數,而是時間。不同系統中,MSL 定義的大小不一樣,RFC 規定,MSL = 2 分鐘,而實際實現中,通常是 30 秒、1 分鐘。盡管 MSL 的單位是時間而不是跳數,我們仍然假設:具有最大跳數(255)的報文在網絡中存在的時間不可能超過 MSL 秒。

當 TCP 協議進入 TIME_WAIT 狀態時,必須要在這個狀態停留 2 倍 MSL 的時間。那為什么要等待這個 2MSL 時間呢?目的是什么?

面試tcp ip問什么(面試需要知道的)(18)

圖13 TCP 三次握手與四次揮手狀態遷移

主要有兩個原因:

  • 可以防止連接終止的最后一個 ACK 丟失。假設最后一個 ACK 在到達對端時恰好消失,此時對端已經等待了一個 RTT(報文段往返時間),于是進行重傳最后一個 FIN,經過 0.5RTT 后到達對端。
  • 等待 2MSL 的第二個原因

假設在 ip1:port1 和 ip2:port2 建立了一個連接 A,發送完數據后關閉連接 A。如果沒有 TIME_WAIT 狀態,我們又立即在 ip1:port1 和 ip2:port2 建立了一個連接 B(雖然這種事情概率很小,但是仍然存在)。

很不幸的是,連接 A 它有一個重復的 TCP 段被連接 B 收到了,然而連接 B 并不知道這個 TCP 段是連接 A 中的舊報文,這會造成錯誤!

如果有了 TIMEWAIT 狀態,等待 2MSL,就足以讓連接 A 中重復的報文在網絡中消逝;另一方面,TCP 協議規定,處于 TIMEWAIT 狀態的端口,是無法建立新的連接的。這樣就保證了每成功建立一個新連接時,舊連接中的重復 TCP 段都已消逝。

7.1 TIME_WAIT 影響

如果 TCP 處于 TIME_WAIT 狀態,會進行 2MSL 時間的等待,在這個時間內,定義此連接的本地端口不能再次使用。比如有一個連接:

(LOCAL 192.168.80.130:5050, FOREIGN 192.168.166.107:40891) state: TIME_WAIT

那么對于 192.168.80.130 這個主機來說,在 2MSL 時間內,5050 端口都不能再次被使用。

一般來說,主動關閉一方都是客戶端,客戶端建立連接時的端口號都是由系統自動分配的,這并沒什么影響,這個端口被占用了,那就再分配一個其它端口就是了。

但是對于服務器來說,它使用的端口是熟知端口(公開出去的端口),如果它是主動關閉一方,進入了 TIME_WAIT 狀態,那么 2MSL 時間內,這個服務器都無法啟動。如果再次啟動,會提示 Address already in use 的錯誤。

實際上,現在的 Linux 提供了一種方法,它可以通過給 socket 指定選項 SO_REUSEADDR 允許端口重用。

需要注意的是,即使服務器或客戶端綁定了處于 2MSL 的端口,RFC 規定它們也不能建立連接。可惜的是,大多系統實現并未遵守這個規定:

  • 如果服務器重用了處于 2MSL 端口,它仍然可以接收連接請求并連接成功(這違反了協議)
  • 如果客戶端重用了處于 2MSL 端口,它建立連接時仍然會失敗。(沒有違反協議)
8. TCP 首部字段有哪些?

經過前面幾個小時的實驗和學習,接下來嘗試著不看圖,分析 TCP 首部字段。

這個問題,顯然不是死記硬背。我們試著從 TCP 擁有的特性和相關的算法試著來分析,作為 TCP 協議,它至少擁有哪些字段。

  • 端口(共 4 字節 )

TCP 是一個端到端的協議,我們知道,要建立 TCP 連接,必須要知道對端的 IP 地址和端口。因此“端口”肯定是 TCP 首部必須的字段,但是 IP 地址不是,因為 IP 地址在三層的網絡層。端口包含源端口和目的端口,因此這兩個字段是 TCP 首部必須有的,即 source port & dest port。

  • 序號(4字節)

TCP 是一個面向字節流的協議,你發的數據是什么順序,對方接收到的就是什么順序。為了保證這一點,TCP 需要對發送的每個字節的數據進行編號,因此 TCP 首部一定有關于數據編號的字段。因為一個 TCP 數據包一次會攜帶若干字節的數據,不可能對每個字節進行編號,在實際協議中,TCP 只對第一個字節數據進行編號,剩下的可以根據第一字節編號進行推算。

關于數據包的長度。TCP 首部中不需要數據包的長度,因為這個長度在 IP 報文中已經攜帶了,TCP 數據包的長度可以根據 IP 報文首部中的長度進行計算。也就是拿 IP 報文中的長度減去 TCP 首部的長度,就是實際數據的長度。

  • 數據偏移(首部長度,4bit)

上面我們說到 TCP 首部長度,一般來說首部長度都是 20 字節。有時候,TCP 首部還可以攜帶一些額外的可選項(optional),這樣首部就超過了 20 字節,很正常嗎,作為軟件開發者,我們都希望我們的協議非常具有彈性。為了能計算 TCP 攜帶的數據的起始位置,數據偏移字段是必不可少的。當然,你也可以叫數據偏移為首部長度完全沒問題啊,這兩者是一回事。另外需要注意的是,這個數據偏移的單位是 4 字節 。比如偏移是 5,說明首部長度就是 20 字節。

  • 確認號(4字節)

我們知道,TCP 是一個可靠的協議。所謂的可靠,是指 TCP 保證發送的報文要能被對方收到,而保證可靠的方法是基于超時重傳算法確認機制。TCP 協議需要對方回復它收到哪些數據,利用之前的序號,就可以做到。因此,TCP 首部還需要一個確認號,來實現可靠傳輸。

  • 標志位(12bit)

TCP 報文有很多種類型,比如三次握手使用的同步包,四次揮手使用的結束包,確認機制使用的確認包,因此,TCP 首部一定有一些字段來描述 TCP 報文的類型。 TCP 首部中,使用一些特殊的比特位來標記報文的類型,如果比特位置 1,就說明它是該類型對應的包。目前有 9 種類型的報文(目前還剩下 3 bit 的保留位,以后可能還會新增新的標志位)。TCP 報文可以同時屬于多種類型,比如可以同時是同步包確認包

目前常見的標志位有:SYN, ACK, PSH, FIN, RST, URG,這也是你在書上能找到的。還有NS, CWR, ECE,知道存在即可,如果你想更深入的了解,可以自行查閱資料。

  • 窗口大小(2字節)

了解過 TCP 的朋友,一定聽說過滑動窗口算法,這個算法主要是用來做流量控制的。什么意思呢?實際上窗口大小是用來描述接收方接收能力的參數,比如我現在最多只能接收 1024 個字節,你發的時候就不要發超過 1024 字節的數據給我,否則我也裝不下,沒地方存儲。因此 TCP 首部中還有 16 比特的字段來記錄窗口大小。

  • 校驗和(2字節)

這個字段即使你不知道,關系也不大。但是對于網絡傳輸來說,校驗和是一個十分常見且普遍使用的東西,TCP 首部有它也不奇怪。

  • 緊急指針(2字節)

這恐怕是目前存在感最低的字段了,現實中使用它的地方很少。在 socket 編程中,稱其為 OOB(out-of-band data),意為連接之外的數據,但實際上它并不真提連接之外的數據,它還是和正常的數據一起被發送出去。”緊急指針“實際上并不緊急,當你發送緊急數據時,只是把 TCP 標志位的緊急標志位設置成 1,同時緊急指針指向對應的緊急數據編號,發送出去,接收方接收到帶緊急標志位的數據時,做一些特殊處理而已。

下面來看一下全貌:

面試tcp ip問什么(面試需要知道的)(19)

最后切記,首部字段是不需要死記硬背的,一定要結合 TCP 自身的特性,來思考 TCP 要想實現這些功能,應當要包含哪些字段。

好,現在再回憶一下,TCP 首部應該有哪些字段?

9. 總結

TCP 內容真的是太多了,本文把一些重要且核心的內容挑了出來,等你掌握了這些基礎后,再去深入學習更多的內容,應該沒什么問題。再回顧一下我們的知識點:

  • 三次握手與四次揮手
  • 三次握手的目的
  • Delay ACK
  • Nagle 算法
  • 擁塞控制
  • MSS/MTU
  • TIME_WAIT
  • TCP 首部字段
  • tcpdump 工具的基本使用
  • 學會使用 python 進行簡單的 socket 編程,用其來學習 tcp 協議

最后,還想說一點,TCP 是一個一直在發展的協議,它內部的算法不是一成不變的,尤其是擁塞控制算法,如今 Linux 內核集成的擁塞控制算法是 CUBIC 算法,最近又有非常火的 BBR 算法(值得你去細讀一番)。

歡迎分享轉載→http://www.avcorse.com/read-228999.html

Copyright ? 2024 有趣生活 All Rights Reserve吉ICP備19000289號-5 TXT地圖HTML地圖XML地圖