2022年4月12日 星期二

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


參考連結:


DP資料包一次傳送多大為好?


在進行UDP程式設計的時候,我們最容易想到的問題就是,一次傳送多少bytes好?

當然,這個沒有唯一答案,相對於不同的系統,不同的要求,其得到的答案是不一樣的,這裡僅對像ICQ一類的傳送聊天訊息的情況作分析,對於其他情況,或許也能得到一點幫助:

首先,我們知道,TCP/IP通常被認為是一個四層協議系統,包括鏈路層,網路層,傳輸層,應用層.UDP屬於運輸層,下面我們由下至上一步一步來看:

乙太網(Ethernet)資料幀的長度必須在46-1500位元組之間,這是由乙太網的物理特性決定的.這個1500位元組被稱為鏈路層的MTU(最大傳輸單元).但這並不是指鏈路層的長度被限制在1500位元組,其實這個MTU指的是鏈路層的資料區.並不包括鏈路層的首部和尾部的18個位元組.所以,事實上,這個1500位元組就是網路層IP資料包的長度限制.因為IP資料包的首部為20位元組,所以IP資料包的資料區長度最大為1480位元組.而這個1480位元組就是用來放TCP傳來的TCP報文段或UDP傳來的UDP資料包的.又因為UDP資料包的首部8位元組,所以UDP資料包的資料區最大長度為1472位元組.這個1472位元組就是我們可以使用的位元組數。:)

當我們傳送的UDP資料大於1472的時候會怎樣呢?這也就是說IP資料包大於1500位元組,大於MTU.這個時候傳送方IP層就需要分片(fragmentation).把資料包分成若干片,使每一片都小於MTU.而接收方IP層則需要進行資料包的重組.這樣就會多做許多事情,而更嚴重的是,由於UDP的特性,當某一片資料傳送中丟失時,接收方便無法重組資料包.將導致丟棄整個UDP資料包。

因此,在普通的區域網環境下,我建議將UDP的資料控制在1472位元組以下為好.

進行Internet程式設計時則不同,因為Internet上的路由器可能會將MTU設為不同的值.如果我們假定MTU為1500來傳送資料的,而途經的某個網路的MTU值小於1500位元組,那麼系統將會使用一系列的機制來調整MTU值,使資料包能夠順利到達目的地,這樣就會做許多不必要的操作.鑑於Internet上的標準MTU值為576位元組,所以我建議在進行Internet的UDP程式設計時.最好將UDP的資料長度控制元件在548位元組(576-8-20)以內.


理論上,IP資料包的最大長度是65535位元組,這是由IP首部16位元總長度欄位所限制的。去除20位元組的IP首部和8個位元組的UDP首部,UDP資料包中使用者資料的最長長度為65507位元組。但是,大多數實現所提供的長度比這個最大值小。

我們將遇到兩個限制因素。第一,應用程式可能會受到其程式介面的限制。socket API提供了一個可供應用程式呼叫的函式,以設定接收和傳送快取的長度。對於UDP socket,這個長度與應用程式可以讀寫的最大UDP資料包的長度直接相關。現在的大部分系統都預設提供了可讀寫大於8192位元組的UDP資料包(使用這個預設值是因為8192是NFS讀寫使用者資料數的預設值)。

第二個限制來自於TCP/IP的核心實現。可能存在一些實現特性(或差錯),使IP資料包長度小於65535位元組。

在SunOS 4.1.3下使用環回介面的最大IP資料包長度是32767位元組。比它大的值都會發生差錯。

但是從BSD/386到SunOS 4.1.3的情況下,Sun所能接收到最大IP資料包長度為32786位元組(即32758位元組使用者資料)。

在Solaris 2.2下使用環回介面,最大可收發IP資料包長度為65535位元組。

從Solaris 2.2到AIX 3.2.2,傳送的最大IP資料包長度可以是65535位元組。很顯然,這個限制與源端和目的端的實現有關。

主機必須能夠接收最短為576位元組的IP資料包。在許多UDP應用程式的設計中,其應用程式資料被限制成512位元組或更小,因此比這個限制值小。

由於IP能夠傳送或接收特定長度的資料包並不意味著接收應用程式可以讀取該長度的資料。因此,UDP程式設計介面允許應用程式指定每次返回的最大位元組數。如果接收到的資料包長度大於應用程式所能處理的長度,那麼會發生什麼情況呢?不幸的是,該問題的答案取決於程式設計介面和實現。

典型的Berkeley版socket API對資料包進行截斷,並丟棄任何多餘的資料。應用程式何時能夠知道,則與版本有關(4.3BSD Reno及其後的版本可以通知應用程式資料包被截斷)。

SVR4下的socket API(包括Solaris 2.x) 並不截斷資料包。超出部分資料在後面的讀取中返回。它也不通知應用程式從單個UDP資料包中多次進行讀取操作。TLI API不丟棄資料。相反,它返回一個標誌表明可以獲得更多的資料,而應用程式後面的讀操作將返回資料包的其餘部分。在討論TCP時,我們發現它為應用程式提供連續的位元組流,而沒有任何資訊邊界。TCP以應用程式讀操作時所要求的長度來傳送資料,因此,在這個介面下,不會發生資料丟失。


參考連結:

沒有留言:

張貼留言