TCP/IP具體解釋--TCP/UDP優化設置總結& MTU的相關介紹

首先要看TCP/IP協議,涉及到四層:鏈路層,網絡層。傳輸層,應用層。linux

   
當中以太網(Ethernet)的數據幀在鏈路層   
IP包在網絡層   
TCP或UDP包在傳輸層   
TCP或UDP中的數據(Data)在應用層   
它們的關係是 數據幀{IP包{TCP或UDP包{Data}}}   
---------------------------------------------------------------------------------
在應用程序中咱們用到的Data的長度最大是多少,直接取決於底層的限制。   
咱們從下到上分析一下:   
1.在鏈路層,由以太網的物理特性決定了數據幀的長度爲(46+18)-(1500+18),當中的18是數據幀的頭和尾,也就是說數據幀的內容最大爲1500(不包含幀頭和幀尾)。即MTU(Maximum Transmission Unit)爲1500;  
2.在網絡層。因爲IP包的首部要佔用20字節,因此這的MTU爲1500-20=1480; 
3.在傳輸層,對於UDP包的首部要佔用8字節。因此這的MTU爲1480-8=1472。   
因此,在應用層,你的Data最大長度爲1472。算法

(當咱們的UDP包中的數據多於MTU(1472)時,發送方的IP層需要分片fragmentation進行傳輸,而在接收方IP層則需要進行數據報重組,由於UDP是不可靠的傳輸協議。假設分片丟失致使重組失敗。將致使UDP數據包被丟棄)。數據庫

  
從上面的分析來看。在普通的局域網環境下,UDP的數據最大爲1472字節最好(避免分片重組)。   
但在網絡編程中。Internet中的路由器可能有設置成不一樣的值(小於默認值),Internet上的標準MTU值爲576。因此Internet的UDP編程時數據長度最好在576-20-8=548字節之內。
---------------------------------------------------------------------------------  
MTU對咱們的UDP編程很是重要。那怎樣查看路由的MTU值呢?   
對於windows OS: ping -f -l   如:ping -f -l 1472 192.168.0.1   
假設提示:Packets needs to be fragmented but DF set.   則代表MTU小於1500,不斷改小data_length值,可以終於測算出gateway的MTU值;   
對於linux OS: ping -c -M do -s   如: ping -c 1 -M do -s 1472 192.168.0.1   
假設提示 Frag needed and DF set……   則代表MTU小於1500。可以再測以推算gateway的MTU。編程

 

原理:ping程序使用ICMP報文。ICMP報文首部佔8字節。IP數據報首部佔20字節,所以在數據大小基礎上加上28字節爲MTU值。

--------------------------------------------------------------------------------- windows

IP數據包的最大長度是64K字節(65535),因爲在IP包頭中用2個字節描寫敘述報文長度,2個字節所能表達的最大數字就是65535。  
    
因爲IP協議提供爲上層協議切割和重組報文的功能,所以傳輸層協議的數據包長度原則上來講沒有限制。
緩存

實際上限制仍是有的,因爲IP包的標識字段終究不可能無限長,依照IPv4。好像上限應該是4G(64K*64K)。網絡

依靠這樣的機制。TCP包頭中就沒有「包長度」字段。而全然依靠IP層去處理分幀。socket

這就是爲何TCP常常被稱做一種「流協議」的緣由。開發人員在使用TCP服務的時候,沒必要去關心數據包的大小。僅僅需講SOCKET看做一條數據流的入口。往裏面放數據就是了,TCP協議自己會進行擁塞/流量控制。函數

  
    
UDP則與TCP不一樣,UDP包頭內有總長度字段。相同爲兩個字節,所以UDP數據包的總長度被限制爲65535,這樣剛好可以放進一個IP包內,使得UDP/IP協議棧的實現很easy和高效。65535再減去UDP頭自己所佔領的8個字節。UDP服務中的最大有效載荷長度僅爲65527。post

這個值也就是你在調用getsockopt()時指定SO_MAX_MSG_SIZE所獲得返回值,不論什麼使用SOCK_DGRAM屬性的socket,一次send的數據都不能超過這個值,不然一定獲得一個錯誤。  
    
那麼,IP包提交給下層協議時將會獲得如何的處理呢?這就取決於數據鏈路層協議了,通常的數據鏈路層協議都會負責將IP包切割成更小的幀,而後在目的端重組它。在EtherNet上,數據鏈路幀的大小如以上幾位大俠所言。而假設是IP   over   ATM,則IP包將被切分紅一個一個的ATM   Cell,大小爲53字節。

 

一些典型的MTU值: 

網絡:                                    MTU字節
超通道                                  65535
16Mb/s信息令牌環(IBM)               17914
4Mb/s令牌環(IEEE802.5)              4464
FDDI                                   4352
以太網                                  1500
IEEE802.3/802.2                         1492
X.25                                    576
點對點(低時延)                         296

    路徑MTU:假設兩臺主機之間的通訊要經過多個網絡,那麼每個網絡的鏈路層就可能有不一樣的MTU。重要的不是兩臺主機所在網絡的MTU的值,重要的是兩臺通訊主機路徑中的最小MTU。它被稱做路徑MTU。

Tcp傳輸中的nagle算法

  TCP/IP協議中。無論發送多少數據。老是要在數據前面加上協議頭,同一時候,對方接收到數據。也需要發送ACK表示確認。爲了儘量的利用網絡帶寬。TCP老是但願儘量的發送足夠大的數據。

(一個鏈接會設置MSS參數,所以。TCP/IP但願每次均可以以MSS尺寸的數據塊來發送數據)。

Nagle算法就是爲了儘量發送大塊數據,避免網絡中充斥着不少小數據塊。

      Nagle算法的基本定義是隨意時刻,最多僅僅能有一個未被確認的小段。

 所謂「小段」,指的是小於MSS尺寸的數據塊,所謂「未被確認」。是指一個數據塊發送出去後,沒有收到對方發送的ACK確認該數據已收到。

1. Nagle算法的規則:

      (1)假設包長度達到MSS,則贊成發送。

      (2)假設該包括有FIN。則贊成發送。

      (3)設置了TCP_NODELAY選項,則贊成發送。

      (4)未設置TCP_CORK選項時,若所有發出去的小數據包(包長度小於MSS)均被確認,則贊成發送;

      (5)上述條件都未知足,但發生了超時(通常爲200ms),則立刻發送。

     Nagle算法僅僅贊成一個未被ACK的包存在於網絡,它並沒有論包的大小,所以它其實就是一個擴展的停-等協議。僅僅只是它是基於包停-等的,而不是基於字節停-等的。Nagle算法全然由TCP協議的ACK機制決定,這會帶來一些問題,比方假設對端ACK回覆很是快的話,Nagle其實不會拼接太多的數據包,儘管避免了網絡擁塞。網絡總體的利用率依舊很是低。

      Nagle算法是silly window syndrome(SWS)預防算法的一個半集。SWS算法預防發送少許的數據,Nagle算法是其在發送方的實現。而接收方要作的時不要通告緩衝空間的很是小增加。不通知小窗體,除非緩衝區空間有顯著的增加。這裏顯著的增加定義爲全然大小的段(MSS)或增加到大於最大窗體的一半。

 注意:BSD的實現是贊成在空暇連接上發送大的寫操做剩下的最後的小段,也就是說,當超過1個MSS數據發送時,內核先依次發送完n個MSS的數據包,而後再發送尾部的小數據包,其間再也不延時等待。

(若是網絡不堵塞且接收窗體足夠大)。

     舉個樣例,一開始client端調用socket的write操做將一個int型數據(稱爲A塊)寫入到網絡中,由於此時鏈接是空暇的(也就是說尚未未被確認的小段),所以這個int型數據會被當即發送到server端,接着,client端又調用write操做寫入‘\r\n’(簡稱B塊)。這個時候。A塊的ACK沒有返回。因此可以以爲已經存在了一個未被確認的小段,因此B塊沒有當即被髮送,一直等待A塊的ACK收到(大概40ms以後),B塊才被髮送。整個過程如圖所看到的:

      這裏還隱藏了一個問題,就是A塊數據的ACK爲何40ms以後才收到?這是因爲TCP/IP中不惟獨nagle算法。另外一個TCP確認延遲機制 。當Server端收到數據以後,它並不會當即向client端發送ACK,而是會將ACK的發送延遲一段時間(若是爲t)。它但願在t時間內server端會向client端發送應答數據,這樣ACK就行和應答數據一塊兒發送,就像是應答數據捎帶着ACK過去。在我以前的時間中,t大概就是40ms。這就解釋了爲何'\r\n'(B塊)老是在A塊以後40ms才發出。

       固然。TCP確認延遲40ms並不是一直不變的。TCP鏈接的延遲確認時間通常初始化爲最小值40ms,隨後依據鏈接的重傳超時時間(RTO)、上次收到數據包與本次接收數據包的時間間隔等參數進行不斷調整。

另外可以經過設置TCP_QUICKACK選項來取消確認延遲。

      關於TCP確認延遲的具體介紹可參考:http://blog.csdn.net/turkeyzhou/article/details/6764389

2. TCP_NODELAY 選項

      默認狀況下,發送數據採用Negale 算法。這樣儘管提升了網絡吞吐量,但是實時性卻減小了。在一些交互性很是強的應用程序來講是不一樣意的,使用TCP_NODELAY選項可以禁止Negale 算法。

      此時。應用程序向內核遞交的每個數據包都會立刻發送出去。

需要注意的是,儘管禁止了Negale 算法。但網絡的傳輸仍然受到TCP確認延遲機制的影響。

3. TCP_CORK 選項

     所謂的CORK就是塞子的意思,形象地理解就是用CORK將鏈接塞住,使得數據先不發出去,等到拔去塞子後再發出去。設置該選項後。內核會盡力把小數據包拼接成一個大的數據包(一個MTU)再發送出去,固然若必定時間後(通常爲200ms,該值尚待確認),內核仍然沒有組合成一個MTU時也必須發送現有的數據(不可能讓數據一直等待吧)。

      然而。TCP_CORK的實現可能並不像你想象的那麼完美,CORK並不會將鏈接全然塞住。

內核事實上並不知道應用層究竟何時會發送第二批數據用於和第一批數據拼接以達到MTU的大小。所以內核會給出一個時間限制,在該時間內沒有拼接成一個大包(努力接近MTU)的話,內核就會無條件發送。也就是說若應用層程序發送小包數據的間隔不夠短時,TCP_CORK就沒有一點做用,反而失去了數據的實時性(每個小包數據都會延時必定時間再發送)。

4. Nagle算法與CORK算法差異

     Nagle算法和CORK算法很是相似。但是它們的着眼點不同,Nagle算法主要避免網絡因爲太多的小包(協議頭的比例很是之大)而擁塞,而CORK算法則是爲了提升網絡的利用率,使得總體上協議頭佔用的比例儘量的小。如此看來這兩者在避免發送小包上是一致的,在用戶控制的層面上。Nagle算法全然不受用戶socket的控制,你僅僅能簡單的設置TCP_NODELAY而禁用它,CORK算法相同也是經過設置或者清除TCP_CORK使能或者禁用之,然而Nagle算法關心的是網絡擁塞問題。僅僅要所有的ACK回來則發包。而CORK算法卻可以關心內容,在先後數據包發送間隔很是短的前提下(很是重要。不然內核會幫你將分散的包發出),即便你是分散發送多個小數據包,你也可以經過使能CORK算法將這些內容拼接在一個包內,假設此時用Nagle算法的話,則可能作不到這一點。

    實際上Nagle算法並不是很是複雜。他的主要職責是數據的累積,實際上有兩個門檻:一個就是緩 衝區中的字節數達到了必定量,還有一個就是等待了必定的時間(通常的Nagle算法都是等待200ms)。這兩個門檻的不論什麼一個達到都必須發送數據了。通常 狀況下。假設數據流量很是大,第二個條件是永遠不會起做用的,但當發送小的數據包時,第二個門檻就發揮做用了。防止數據被無限的緩存在緩衝區不是好事情哦。 瞭解了TCP的Nagle算法的原理以後咱們可以本身動手來實現一個相似的算法了,在動手以前咱們還要記住一個重要的事情,也是咱們動手實現Nagle算 法的主要動機就是我想要緊急發送數據的時候就要發送了,因此對於上面的兩個門檻以外還的添加一個門檻就是緊急數據發送。

    對於我現在每秒鐘10次數據發送。每次數據發送量固定在85~100字節的應用而言。假設採用默認的開啓Nagle算法。我在發送端,固定每幀數據85個,間隔100ms發送一次,我在接受端(堵塞方式使用)接受的數據是43 138交替出現,可能就是這個算法的時間閾值問題,假設關閉Nagle算法,在接收端就可以保證數據每次接收到的都是85幀。

    Nagle算法適用於小包、高延遲的場合,而對於要求交互速度的b/s或c/s就不合適了。

socket在建立的時候。默認都是使用Nagle算法的,這會致使交互速度嚴重降低,因此需要setsockopt函數來設置TCP_NODELAY爲1.只是取消了Nagle算法,就會致使TCP碎片增多。效率可能會減小。

關閉nagle算法,以避免影響性能。因爲控制時控制端要發送很是多數據量很是小的數據包,需要當即發送。

 const char chOpt = 1;

int nErr = setsockopt(pContext->m_Socket, IPPROTO_TCP, TCP_NODELAY, &chOpt, sizeof(char));

if (nErr == -1)

{

    TRACE(_T("setsockopt() error\n"),WSAGetLastError());

    return;

}

setsockopt(sockfd, SOL_TCP, TCP_CORK, &on, sizeof(on)); //set TCP_CORK

 

TCP傳輸小數據包效率問題

摘要:當使用TCP傳輸小型數據包時。程序的設計是至關重要的。假設在設計方案中不正確TCP數據包的
延遲應答,Nagle算法。Winsock緩衝做用引發重視,將會嚴重影響程序的性能。這篇文章討論了這些
問題,列舉了兩個案例。給出了一些傳輸小數據包的優化設計方案。

背景:當Microsoft TCP棧接收到一個數據包時,會啓動一個200毫秒的計時器。當ACK確認數據包
發出以後,計時器會復位,接收到下一個數據包時。會再次啓動200毫秒的計時器。爲了提高應用程序
在內部網和Internet上的傳輸性能,Microsoft TCP棧使用瞭如下的策略來決定在接收到數據包後
何時發送ACK確認數據包:
一、假設在200毫秒的計時器超時以前。接收到下一個數據包。則立刻發送ACK確認數據包。
二、假設當前剛好有數據包需要發給ACK確認信息的接收端,則把ACK確認信息附帶在數據包上立刻發送。


三、當計時器超時,ACK確認信息立刻發送。
爲了不小數據包擁塞網絡。Microsoft TCP棧默認啓用了Nagle算法,這個算法能夠將應用程序屢次
調用Send發送的數據拼接起來,當收到前一個數據包的ACK確認信息時,一塊兒發送出去。如下是Nagle
算法的例外狀況:
一、假設Microsoft TCP棧拼接起來的數據包超過了MTU值,這個數據會立刻發送,而不等待前一個數據
包的ACK確認信息。在以太網中,TCP的MTU(Maximum Transmission Unit)值是1460字節。
二、假設設置了TCP_NODELAY選項。就會禁用Nagle算法。應用程序調用Send發送的數據包會立刻被
投遞到網絡,而沒有延遲。
爲了在應用層優化性能,Winsock把應用程序調用Send發送的數據從應用程序的緩衝區拷貝到Winsock
內核緩衝區。Microsoft TCP棧利用相似Nagle算法的方法,決定何時才實際地把數據投遞到網絡。


內核緩衝區的默認大小是8K,使用SO_SNDBUF選項,可以改變Winsock內核緩衝區的大小。假設有必要的話。
Winsock能緩衝大於SO_SNDBUF緩衝區大小的數據。在絕大多數狀況下,應用程序完畢Send調用只代表數據
被拷貝到了Winsock內核緩衝區,並不能說明數據就實際地被投遞到了網絡上。

惟一一種例外的狀況是:
經過設置SO_SNDBUT爲0禁用了Winsock內核緩衝區。

Winsock使用如下的規則來嚮應用程序代表一個Send調用的完畢:
一、假設socket仍然在SO_SNDBUF限額內,Winsock複製應用程序要發送的數據到內核緩衝區。完畢Send調用。


二、假設Socket超過了SO_SNDBUF限額並且先前僅僅有一個被緩衝的發送數據在內核緩衝區,Winsock複製要發送
的數據到內核緩衝區,完畢Send調用。
三、假設Socket超過了SO_SNDBUF限額並且內核緩衝區有不只僅一個被緩衝的發送數據,Winsock複製要發送的數據
到內核緩衝區,而後投遞數據到網絡。直到Socket降到SO_SNDBUF限額內或者僅僅剩餘一個要發送的數據,才
完畢Send調用。

案例1
一個Winsock TCPclient需要發送10000個記錄到Winsock TCP服務端,保存到數據庫。記錄大小從20字節到100
字節不等。

對於簡單的應用程序邏輯,可能的設計方案例如如下:
一、client以堵塞方式發送。服務端以堵塞方式接收。
二、client設置SO_SNDBUF爲0。禁用Nagle算法,讓每個數據包單獨的發送。
三、服務端在一個循環中調用Recv接收數據包。給Recv傳遞200字節的緩衝區以便讓每個記錄在一次Recv調用中
被獲取到。

性能:
在測試中發現。client每秒僅僅能發送5條數據到服務段。總共10000條記錄,976K字節左右。用了半個多小時
才全部傳到server。

分析:
因爲client沒有設置TCP_NODELAY選項,Nagle算法強制TCP棧在發送數據包以前等待前一個數據包的ACK確認
信息。

然而,client設置SO_SNDBUF爲0,禁用了內核緩衝區。所以,10000個Send調用僅僅能一個數據包一個數據
包的發送和確認,由於下列緣由,每個ACK確認信息被延遲200毫秒:
一、當server獲取到一個數據包,啓動一個200毫秒的計時器。
二、服務端不需要向client發送不論什麼數據,因此。ACK確認信息不能被髮回的數據包順路攜帶。
三、client在沒有收到前一個數據包的確認信息前,不能發送數據包。
四、服務端的計時器超時後。ACK確認信息被髮送到client。

怎樣提升性能:
在這個設計中存在兩個問題。第一,存在延時問題。client需要能夠在200毫秒內發送兩個數據包到服務端。
因爲client默認狀況下使用Nagle算法,應該使用默認的內核緩衝區,不該該設置SO_SNDBUF爲0。一旦TCP
棧拼接起來的數據包超過MTU值。這個數據包會立刻被髮送,不用等待前一個ACK確認信息。第二,這個設計
方案對每一個如此小的的數據包都調用一次Send。

發送這麼小的數據包是不很是有效率的。在這樣的狀況下。應該
把每個記錄補充到100字節並且每次調用Send發送80個記錄。爲了讓服務端知道一次總共發送了多少個記錄,
client可以在記錄前面帶一個頭信息。

案例二:
一個Winsock TCPclient程序打開兩個鏈接和一個提供股票報價服務的Winsock TCP服務端通訊。

第一個鏈接
做爲命令通道用來傳輸股票編號到服務端。第二個鏈接做爲數據通道用來接收股票報價。兩個鏈接被創建後,
client經過命令通道發送股票編號到服務端。而後在數據通道上等待返回的股票報價信息。

client在接收到第一
個股票報價信息後發送下一個股票編號請求到服務端。client和服務端都沒有設置SO_SNDBUF和TCP_NODELAY
選項。

性能:
測試中發現,client每秒僅僅能獲取到5條報價信息。

分析:

這個設計方案一次僅僅贊成獲取一條股票信息。

第一個股票編號信息經過命令通道發送到服務端。立刻接收到
服務端經過數據通道返回的股票報價信息。而後。client立刻發送第二條請求信息。send調用立刻返回,
發送的數據被拷貝到內核緩衝區。然而,TCP棧不能立刻投遞這個數據包到網絡。因爲沒有收到前一個數據包的
ACK確認信息。200毫秒後。服務端的計時器超時,第一個請求數據包的ACK確認信息被髮送回client。client
的第二個請求包才被投遞到網絡。第二個請求的報價信息立刻從數據通道返回到client,因爲此時。client的
計時器已經超時,第一個報價信息的ACK確認信息已經被髮送到服務端。

這個過程循環發生。

怎樣提升性能:
在這裏。兩個鏈接的設計是沒有必要的。

假設使用一個鏈接來請求和接收報價信息,股票請求的ACK確認信息會
被返回的報價信息立刻順路攜帶回來。

要進一步的提升性能,client應該一次調用Send發送多個股票請求,服務端
一次返回多個報價信息。假設由於某些特殊緣由必須要使用兩個單向的鏈接,client和服務端都應該設置TCP_NODELAY
選項,讓小數據包立刻發送而不用等待前一個數據包的ACK確認信息。

提升性能的建議:
上面兩個案例說明了一些最壞的狀況。當設計一個方案解決大量的小數據包發送和接收時,應該遵循下面的建議:
一、假設數據片斷不需要緊急傳輸的話。應用程序應該將他們拼接成更大的數據塊,再調用Send。因爲發送緩衝區
很是可能被拷貝到內核緩衝區,因此緩衝區不該該太大,一般比8K小一點點是很是有效率的。

僅僅要Winsock內核緩衝區
獲得一個大於MTU值的數據塊,就會發送若干個數據包。剩下最後一個數據包。

發送方除了最後一個數據包,都不會
被200毫秒的計時器觸發。
二、假設可能的話,避免單向的Socket數據流接連。


三、不要設置SO_SNDBUF爲0。除非想確保數據包在調用Send完畢以後立刻被投遞到網絡。其實,8K的緩衝區適合大多數
狀況。不需要又一次改變。除非新設置的緩衝區通過測試的確比默認大小更高效。
四、假設傳輸數據不用保證可靠性,使用UDP。

相關文章
相關標籤/搜索