tcp/ip 需要知道的一些事

weixin_34378969發表於2016-12-19

本文主要是最近看了以下《tcp/ip詳解 卷1》的部分小結,個人最感興趣的是 經典問題 40ms 延時 的問題!
個人建議著重看19-24章節。每章都有乾貨。

單播、多播、廣播、組播、泛播概念區分

參考

ICMP

ICMP的作用

  • 差錯控制
  • “Ping”的過程實際上是ICMP協議工作的過程。還有其他的網路命令如跟蹤路由的Traceroute命令也是

tcp

幾個概念

msl 最長報文段存活時間
mss 最大報文段長度 ,主要是為了防止報文端被分段傳送。 如果超過1mss 將會被分段傳送。
定時器

  1. 重傳定時器
  2. 堅持(presist)定時器 視窗探查
  3. 保活(keepalive)定時器 就是心跳檢測
  4. 2MSL定時器 測量一個連線處於TIME_WAIT狀態的時間。
  • 狀態流轉


    1855493-a85a23f6d5996712.jpg

常見的情況,比如server關了,埠依然佔用,其實就是處於TIME_WAIT狀態 ,2MSL超時,用於超時重傳最後的ack。

  • 接收視窗和傳送視窗的區別
    接收視窗是接收方發給傳送方的,(就是抓包看到的win= ),
    傳送視窗是由sender根據receiver的接收視窗以及當前已傳送未ack的資料來自己計算的。tcp抓包看不到
  • tcp 滑動視窗和擁塞視窗的區別
    滑動視窗:滑動視窗不是真實存在的視窗,需要sender和recever共同作用。通告視窗(window offered by receiver) ,也就是我們抓包看到的win=xxx,是接收方使用的流量控制。
1855493-84abd03e1f94e1ee.png
Paste_Image.png
1855493-3f44757f235083d3.png
Paste_Image.png

我們來分析一下1的情況,當sender 連續傳送資料給receiver的時候,如果receiver的應用層來不及把資料從核心recv buffer拷貝到應用buffer,那麼recv buffer會爆滿,這時候receiver ack win=0。然後sender會把資料一直拷貝到send buffer而send buffer不發給receiver,直到receiver ack win> 0。

由此可見,通告視窗win就是recv buffer的剩餘空間。

具體例子的分析可見 《tcp/ip 詳解1》p213 。我們可以簡單根據這個抓包的時序對滑動視窗進行分析。尤其我們看到 報文段8 win=3072.說明在receiver的recv buffer中還有1024個位元組未被應用層buffer拷貝過去。因此在圖2中可以看到的滑動視窗的通告視窗就是3072。


1855493-b4e376e0c1a118d0.png
圖1
1855493-99c20e8bcf620950.png
圖2

其實我們仔細想想看這裡貌似存在一個缺陷,如果receiver win=0 之後 通告的win>0 的ack報文段(比如圖1的報文段8)丟失了,那sender豈不是要一直等下去?
這個問題設計者早就發現了,採用了一個tcp堅持定時器。 sender週期性的向receiver 查詢,以便發現視窗是否已經增大。 這些sender發出去的報文段叫做視窗探查。

**tcp window scale ** :

由於緩衝區大小在TCP頭部只有16位來表示,所以它的最大值是65536,但是對於一些情況來說需要使用更大的滑動視窗,這時候就要使用擴充套件的滑動視窗,如光纖高速通訊網路,或者是衛星長連線網路,需要視窗儘可能的大。這時會使用擴充套件的32位的滑動視窗大小。

shift count 在tcp三次握手的時候確認
最終的calculated window size =2^7 × 227 =128 * 227 都可在上面找到對應的數值。

擁塞視窗:是傳送端維護的一個視窗,是傳送方使用的流量控制。主要就是維護cwnd。下面可以看到分析。

慢啟動和擁塞避免

超時重傳採用“指數退避演算法”
慢啟動和擁塞避免是兩個獨立的演算法,但通常一起作用。
需要維持的變數:

  • 擁塞視窗 cwnd(預設初始化為1)
  • 慢啟動門限 ssthresh(預設初始化為65535位元組)

擁塞演算法:

1855493-3b039bc21b3d4a71.png
Paste_Image.png

當擁塞發生時(超時或收到重複確認) ssthresh被設定為當前視窗大小的一半( cwnd和接收方通告視窗大小的最小值,但最少為 2個報文段) 。此外,如果是超時引起了擁塞,則c w n d被設定為1個報文段(這就是慢啟動) 。

1855493-69eb68f23070c5fe.png
Paste_Image.png

前半段是慢啟動(由cwnd=1開始可判斷),後半段是擁塞避免。此時ssthresh是16個報文段大小。
擁塞避免的公式:

1855493-40ca7783ffa012c6.png
Paste_Image.png

圖中都是收到重複ack的 擁塞避免。從圖中我們可以清晰的看到每個序號下降的地方就是一次重傳,共有3處,而且每處都只有一個下落點,因此可以得出每處只重傳了一個報文段。
對於cwnd的曲線,在每處發現重傳後,就


1855493-c1a9d57554df9ec4.png
Paste_Image.png

詳細分析參看《tcp/ip詳解》21章

延遲確認和Nagle演算法

延遲確認:接收端收到資料之後,並不立即傳送ACK確認收到資料,而是延遲傳送ACK,等待一段時間,以期望和沿該方向傳送的資料一起傳送。是為了提高網路效能,因為少發了幾次,然而,在某些情況下,該技術可以降低應用程式的效能。

Nagle演算法:儘可能傳送大塊資料,避免使網路中充斥小分組,從而減少傳送包的個數來增加網路的利用率。Nagle演算法要求在任意時刻,最多有一個未被確認的分組,在收到確認之前,小分組將被快取在傳送端。直到快取了一定量,等待了一定的時間,或者收到前一個資料的確認,才能傳送出去。由TCP_NODELAY選項控制。

我覺得: 這兩個主要是為了解決傳輸中 連續資料包都很小的情況,為了優化效能,而採用延遲確認和nagle,這樣在發的時候可以屯多一點資料一起發,應答的時候,把資料和ack一起帶過去。但是對於傳輸成塊資料的情況就不太適用了,這兩個預設都是開啟的,要注意適時關閉!!,否則會帶來資料傳輸延遲。所以一般我們實際工作中遇到由200ms以內的延時,可以懷疑下和tcp的延時ack,nagle演算法有無關係。

經典問題 40ms 延時

產生條件

  1. 延遲確認與Nagle演算法相互作用
  2. 延遲確認與擁塞控制相互作用

延遲確認與Nagle演算法相互作用

  • 延時ack

如果收到的資料內容大於一個MSS, 傳送ACK;(大部分情況是這個)
如果收到了接收視窗以外的資料, 傳送ACK;
如果處於quick mode, 傳送ACK;
如果收到亂序的資料, 傳送ACK;
其他, 延遲傳送ACK

延遲ack其實是動態變化的,跟 Pingpong 這個值有關。
Pingpong是一個狀態值, 用來標識當前tcp互動的狀態, 以預測是否是W-R-W-R-W-R這種互動式的通訊模式(互動資料流), 如果處於(=1), 可以用延遲ack。
我們來看以下這個圖。

1855493-a7f77485e36373b9.png

當檢測到是互動資料流時 ,Pingpong=1,啟用了延時ack。當出現一個延遲純ack時取消互動模式 Pingpong=0;

  • Nagle演算法

如果傳送內容大於等於1個MSS, 立即傳送;
如果之前沒有包未被確認, 立即傳送;
如果之前有包未被確認, 快取傳送內容;
如果收到ack, 立即傳送快取的內容。
如果該包含有FIN,則允許傳送;
設定了TCP_NODELAY選項,則允許傳送;

接下來分析以一下下面的情況:

1 00:44:37.878027 IP 172.25.38.135.44792 > 172.25.81.16.9877: S 3512052379:3512052379(0) win 5840 <mss 1448,wscale 7> 
2 00:44:37.878045 IP 172.25.81.16.9877 > 172.25.38.135.44792: S 3581620571:3581620571(0) ack 3512052380 win 5792 <mss 1460,wscale 2> 
3 00:44:37.879080 IP 172.25.38.135.44792 > 172.25.81.16.9877: . ack 1 win 46 
 ...... 
4 00:44:38.885325 IP 172.25.38.135.44792 > 172.25.81.16.9877: P 1321:1453(132) ack 1321 win 86 
5 00:44:38.886037 IP 172.25.81.16.9877 > 172.25.38.135.44792: P 1321:1453(132) ack 1453 win 2310 
6 00:44:38.887174 IP 172.25.38.135.44792 > 172.25.81.16.9877: P 1453:2641(1188) ack 1453 win 102 
7 00:44:38.887888 IP 172.25.81.16.9877 > 172.25.38.135.44792: P 1453:2476(1023) ack 2641 win 2904 
8 00:44:38.925270 IP 172.25.38.135.44792 > 172.25.81.16.9877: . ack 2476 win 118
9 00:44:38.925276 IP 172.25.81.16.9877 > 172.25.38.135.44792: P 2476:2641(165) ack 2641 win 2904 
10 00:44:38.926328 IP 172.25.38.135.44792 > 172.25.81.16.9877: . ack 2641 win 134

前三個報文段是三次握手,可以看出172.25.81.16.9877 為server,172.25.38.135.44792為client
4,5,6,7 是client和server互相互動資料流,並且8 出現了40ms延時ack,這個流程符合我們上面的流程分析。我們可看到延時ack是在client端開啟了,server端開啟了nagle演算法。
在報文段5到達client時,client就知道了這是互動資料流,所以開啟延時ack。當7發給client時,由於cleint並不需要傳送什麼資料給server,所以ack就一直等著定時器超時溢位,我們看到這時候延遲了40ms。
由於8報文段,即位元組序號2476的ack一直沒來,並且server端開啟著nadle演算法(如果之前有包未被確認, 快取傳送內容),所以報文段9 在收到8後才發。
40ms 這個是時間不同系統可能預設不同,rethat 預設40ms

解決延時的方案
對於這個例子而言
client

  1. client端關閉延遲ack
    但是, 每個tcp請求都返回一個ack包, 導致網路包量的增加,需要斟酌
  2. 設定TCP_QUICKACK屬性。 但是需要每次recv後再設定一次。

server

  1. 關閉nagel演算法,即設定TCP_NODELAY。
staticvoid_set_tcp_nodelay(intfd) {
intenable =1;
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,(void*)&enable,sizeof(enable));
}

但這樣會導致網路中出現很多小於1mss的報文段,因此最好能將這些小報文段先快取起來,大於1mss的時候再發出去,這樣這個mss 會被分段,1個正好是1mss,1個小於1mss,cilent連續收到這兩個分段後,判斷接受到的資料大於1mss,因此,立即傳送ack。無延遲。

延遲確認與擁塞視窗相互作用

前提傳送方(關閉Nagle),接收方(啟用延時ack)
同樣分析,即比如接收方mss大小1460 ,傳送方連續發了三個資料大小為100的報文段,此時到達了擁塞視窗cwnd=3,不能在傳送了,那麼接收方接受到300<1460 ,所以接收方也會出現40ms延時ack。
縮短延時方案
參考 http://www.jianshu.com/p/6f795599e4ab * 為什麼是40ms?這個時間能不能調整呢?* 這段。

【參考文章】

總結TCP的一些演算法或者實現方式,在我們平常開發中的應用和可借鑑的地方

  1. 滑動視窗
  2. 超時重傳
  3. 延遲確認和Nagle演算法 :優化延時

[附送 好文推薦]

關注我的公眾號,看更多好文!


1855493-53027a1de10a62c3

相關文章