TCP、UDP資料包大小的限制
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位元組以內。
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。
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函式時,資料長度引數不受限制。而實際上,所指定的這段資料並不一定會一次性發送出去,如果這段資料比較長,會被分段傳送,如果比較短,可能會等待和下一次資料一起傳送。