1、概述
首先要看TCP/IP協議,涉及到四層:鏈路層,網絡層,傳輸層,應用層。
其中以太網(Ethernet)的數據幀在鏈路層
IP包在網絡層
TCP或UDP包在傳輸層
TCP或UDP中的數據(Data)在應用層
它們的關系是 數據幀{IP包{TCP或UDP包{Data}}}
不同的協議層對數據包有不同的稱謂,在傳輸層叫做段(segment),在網絡層叫做數據報(datagram),在鏈路層叫做幀(frame)。數據封裝成幀后發到傳輸介質上,到達目的主機后每層協議再剝掉相應的首部,最后將應用層數據交給應用程序處理。
在應用程序中我們用到的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字節以內。
MSS的主要作用是限制另一端主機發送的數據的長度,同時,主機本身也控制自己發送數據報的長度,這將使以較小MTU連接到一個網絡上的主機避免分段。
MTU的含義: MAC幀內的數據(Payload)字段的最大長度。
2、TCP、UDP數據包最大值的確定
UDP和TCP協議利用端口號實現多項應用同時發送和接收數據。數據通過源端口發送出去,通過目標端口接收。有的網絡應用只能使用預留或注冊的靜態端口;而另外一些網絡應用則可以使用未被注冊的動態端口。因為UDP和TCP報頭使用兩個字節存放端口號,所以端口號的有效范圍是從0到65535。動態端口的范圍是從1024到65535。
MTU最大傳輸單元,這個最大傳輸單元實際上和鏈路層協議有着密切的關系,EthernetII幀的結構DMAC+SMAC+Type+Data+CRC由於以太網傳輸電氣方面的限制,每個以太網幀都有最小的大小64Bytes最大不能超過1518Bytes,對於小於或者大於這個限制的以太網幀我們都可以視之為錯誤的數據幀,一般的以太網轉發設備會丟棄這些數據幀。
由於以太網EthernetII最大的數據幀是1518Bytes這樣,刨去以太網幀的幀頭(DMAC目的MAC地址48bits=6Bytes+SMAC源MAC地址48bits=6Bytes+Type域2Bytes)14Bytes和幀尾CRC校驗部分4Bytes那么剩下承載上層協議的地方也就是Data域最大就只能有1500Bytes這個值我們就把它稱之為MTU。
鏈路層幀的大小 1500(不包括幀頭、幀尾)
UDP 包的大小就應該是 1500 - IP頭(20) - UDP頭(8) = 1472(Bytes)
TCP 包的大小就應該是 1500 - IP頭(20) - TCP頭(20) = 1460 (Bytes)
注*PPPoE所謂PPPoE就是在以太網上面跑“PPP”。隨着寬帶接入(這種寬帶接入一般為Cable Modem或者xDSL或者以太網的接入),因為以太網缺乏認證計費機制而傳統運營商是通過PPP協議來對撥號等接入服務進行認證計費的,所以引入PPPoE。PPPoE導致MTU變小了以太網的MTU是1500,再減去PPP的包頭包尾的開銷(8Bytes),就變成1492。不過目前大多數的路由設備的MTU都為1500。
如果我們定義的TCP和UDP包沒有超過范圍,那么我們的包在IP層就不用分包了,這樣傳輸過程中就避免了在IP層組包發生的錯誤;如果超過范圍,既IP數據報大於1500字節,發送方IP層就需要將數據包分成若干片,而接收方IP層就需要進行數據報的重組。更嚴重的是,如果使用UDP協議,當IP層組包發生錯誤,那么包就會被丟棄。接收方無法重組數據報,將導致丟棄整個IP數據報。UDP不保證可靠傳輸;但是TCP發生組包錯誤時,該包會被重傳,保證可靠傳輸。
UDP數據報的長度是指包括報頭和數據部分在內的總字節數,其中報頭長度固定,數據部分可變。數據報的最大長度根據操作環境的不同而各異。從理論上說,包含報頭在內的數據報的最大長度為65535字節(64K)。
我們在用Socket編程時,UDP協議要求包小於64K。TCP沒有限定,TCP包頭中就沒有“包長度”字段,而完全依靠IP層去處理分幀。這就是為什么TCP常常被稱作一種“流協議”的原因,開發者在使用TCP服務的時候,不必去關心數據包的大小,只需講SOCKET看作一條數據流的入口,往里面放數據就是了,TCP協議本身會進行擁塞/流量控制。
不過鑒於Internet(非局域網)上的標准MTU值為576字節,所以建議在進行Internet的UDP編程時,最好將UDP的數據長度控制在548字節 (576-8-20)以內。
3、TCP、UDP數據包最小值的確定
在用UDP局域網通信時,經常發生“Hello World”來進行測試,但是“Hello World”並不滿足最小有效數據(64-46)的要求,為什么小於18個字節,對方仍然可用收到呢?因為在鏈路層的MAC子層中會進行數據補齊,不足18個字節的用0補齊。但當服務器在公網,客戶端在內網,發生小於18個字節的數據,就會出現接收端收不到數據的情況。
以太網EthernetII規定,以太網幀數據域部分最小為46字節,也就是以太網幀最小是6+6+2+46+4=64。除去4個字節的FCS,因此,抓包時就是60字節。當數據字段的長度小於46字節時,MAC子層就會在數據字段的后面填充以滿足數據幀長不小於64字節。由於填充數據是由MAC子層負責,也就是設備驅動程序。不同的抓包程序和設備驅動程序所處的優先層次可能不同,抓包程序的優先級可能比設備驅動程序更高,也就是說,我們的抓包程序可能在設備驅動程序還沒有填充不到64字節的幀的時候,抓包程序已經捕獲了數據。因此不同的抓包工具抓到的數據幀的大小可能不同。下列是本人分別用wireshark和sniffer抓包的結果,對於TCP 的ACK確認幀的大小一個是54字節,一個是60字節,wireshark抓取時沒有填充數據段,sniffer抓取時有填充數據段。
4、實際應用
用UDP協議發送時,用sendto函數最大能發送數據的長度為:65535- IP頭(20) - UDP頭(8)=65507字節。用sendto函數發送數據時,如果發送數據長度大於該值,則函數會返回錯誤。
用TCP協議發送時,由於TCP是數據流協議,因此不存在包大小的限制(暫不考慮緩沖區的大小),這是指在用send函數時,數據長度參數不受限制。而實際上,所指定的這段數據並不一定會一次性發送出去,如果這段數據比較長,會被分段發送,如果比較短,可能會等待和下一次數據一起發送。
也就是說以鏈路數據幀的長度必須在46-1500字節之間,這個1500字節被稱為鏈路層的MTU(最大傳輸單元)。
超過1500字節怎么辦?
這也就是說IP數據報大於1500字節,大於MTU.這個時候發送方IP層就需要分片(fragmentation).
把數據報分成若干片,使每一片都小於MTU.而接收方IP層則需要進行數據報的重組.
這樣就會多做許多事情,而更嚴重的是,由於UDP的特性,當某一片數據傳送中丟失時,接收方便
無法重組數據報.將導致丟棄整個UDP數據報。
進行Internet編程時則不同,因為Internet上的路由器可能會將MTU設為不同的值.
如果我們假定MTU為1500來發送數據的,而途經的某個網絡的MTU值小於1500字節,那么系統將會使用一系列的機
制來調整MTU值,使數據報能夠順利到達目的地,這樣就會做許多不必要的操作。鑒於Internet上的標准MTU值為576字節,
所以我建議在進行Internet的UDP編程時. 最好將UDP的數據長度控件在548字節(576-8-20)以內.
IP層:
對於IP協議來說,IP包的大小由MTU決定(IP數據包長度就是MTU-28(包頭長度)。 MTU值越大,封包就越大,理論上可增加傳送速率,但 MTU值又不能設得太大,因為封包太大,傳送時出現錯誤的機會大增。一般默認的設置,PPPoE連接的最高MTU值是1492, 而以太網 (Ethernet)的最高MTU值則是1500,而在Internet上,默認的MTU大小是576字節。
那么就一個問題, 用wireshark捕包。 為什么frame那一行是1514bytes?
以太網封裝IP數據包的最大長度是1500字節,也就是說以太網最大幀長應該是以太網首部加上1500,再加上7字節的前導同步碼和1字節的幀開始定界符,具體就是:7字節前導同步嗎+1字節幀開始定界符+6字節的目的MAC+6字節的源MAC+2字節的幀類型+1500+4字節的CRC校驗。
按照上述,最大幀應該是1526字節,但是實際上我們抓包得到的最大幀是1514字節,為什么不是1526字節呢?原因是當數據幀到達網卡時,在物理層上網卡要先去掉前導同步碼和幀開始定界符,然后對幀進行CRC檢驗,如果幀校驗和錯,就丟棄此幀。如果校驗和正確,就判斷幀的目的硬件地址是否符合自己的接收條件(目的地址是自己的物理硬件地址、廣播地址、可接收的多播硬件地址等),如果符合,就將幀交“設備驅動程序”做進一步處理。這時我們的抓包軟件才能抓到數據,因此,抓包軟件抓到的是去掉前導同步碼、幀開始分界符、CRC校驗之外的數據,其最大值是6+6+2+1500=1514。
如下圖:
Preamble |
Destination MAC address |
Source MAC address |
Type/Length |
User Data |
Frame Check Sequence (FCS) |
8 |
6 |
6 |
2 |
46 - 1500 |
4 |
注意:
MSS 是是 傳輸層 概念, 在建立TCP連接過程, 雙方的SYN 報文中定義的 , 通訊雙方會根據雙方提供的MSS值得最小值確定為這次連接的最大MSS值。
后面的數據傳輸都是按照確定的MSS進行傳輸。 MSS會根據物理接口的MTU計算得出 (MSS=MTU-IP頭長度-TCP頭長度)
MTU (max transfer unit) 是是 數據鏈路層 中的概念,需要在 網卡中設置 的,如果IP層有一個數據報要傳,而且數據的長度比鏈路層的MTU還大,那么IP層就需要進行分片(fragmentation),把數據報分成若干片,這樣每一片都小於MTU。
對於一個以太網來說,TCP的最大報文段長度即MSS一般是1460字節(1500(MTU) - 20(IP head) - 20(TCP head) = 1460 Byte),減去12字節的TCP timestamp option,留給TCP正文數據是 1448字節 。另外,TCP流量控制采用了滑動窗口機制,發送窗口的大小要小於min(接收端通告的接收窗口大小,發送端擁塞窗口大小)。
google得知Linux 2.6.39內核之前的TCP實現中,發送端初始擁塞窗口為3,單位是MSS (server機器內核版本未知,估計是2.6.39以前的)。也就是說TCP連接建立后,初始擁塞窗口限制第一次發送的數據長度要小於等於 1448 * 3 = 4344 字節,所以客戶端 第一次recv 調用只收到了4344字節 數據。