這篇文章總結的不錯,轉自:http://hi.baidu.com/to_wait/blog/item/3e855931a5a51717eac4af22.html
首先要看TCP/IP協議,涉及到四層:鏈路層,網絡層,傳輸層,應用層。
其中以太網(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。
---------------------------------------------------------------------------------
IP數據包的最大長度是64K字節(65535),因為在IP包頭中用2個字節描述報文長度,2個字節所能表達的最大數字就是65535。
由於IP協議提供為上層協議分割和重組報文的功能,因此傳輸層協議的數據包長度原則上來說沒有限制。實際上限制還是有的,因為IP包的標識字段終究不可能無限長,按照IPv4,好像上限應該是4G(64K*64K)。依靠這種機制,TCP包頭中就沒有“包長度”字段,而完全依靠IP層去處理分幀。這就是為什么TCP常常被稱作一種“流協議”的原因,開發者在使用TCP服務的時候,不必去關心數據包的大小,只需講SOCKET看作一條數據流的入口,往里面放數據就是了,TCP協議本身會進行擁塞/流量控制。
UDP則與TCP不同,UDP包頭內有總長度字段,同樣為兩個字節,因此UDP數據包的總長度被限制為65535,這樣恰好可以放進一個IP包內,使得UDP/IP協議棧的實現非常簡單和高效。65535再減去UDP頭本身所占據的8個字節,UDP服務中的最大有效載荷長度僅為65527。這個值也就是你在調用getsockopt()時指定SO_MAX_MSG_SIZE所得到返回值,任何使用SOCK_DGRAM屬性的socket,一次send的數據都不能超過這個值,否則必然得到一個錯誤。
那么,IP包提交給下層協議時將會得到怎樣的處理呢?這就取決於數據鏈路層協議了,一般的數據鏈路層協議都會負責將IP包分割成更小的幀,然后在目的端重組它。在EtherNet上,數據鏈路幀的大小如以上幾位大俠所言。而如果是IP over ATM,則IP包將被切分成一個一個的ATM Cell,大小為53字節。
******************************************************************************************************************************
******************************************************************************************************************************
CP提供的是一種面向連接的,可靠的字節流服務,TCP提供可靠性的一種重要的方式就是MSS。通過MSS,應用數據被分割成TCP認為最適合發送的數據塊,由TCP傳遞給IP的信息單位稱為報文段或段(segment)。代表一個TCP socket的結構體struct tcp_sock中有多個成員用於確定應用數據被分割成最大為多大的數據塊較為合適(最大報文段長度MSS)。
我們不難聯想到,跟最大報文段長度最為相關的一個參數是網絡設備接口的MTU,以太網的MTU是1500,基本IP首部長度為20,TCP首部是20,所以MSS的值可達1460(MSS不包括協議首部,只包含應用數據)。
前面的TCP三次握手協議中我們看到,通訊的雙方都通過TCP選項通告了自己期望接收的MSS值,該值直接來源於struct tcp_sock的成員advmss,而這個值直接取自於網絡設備接口的MTU減去IP首部和TCP首部的長度。在本地以太網中可達1460(如果首部都不含選項的話)。而成員rx_opt是一個結構體struct tcp_options_received,它記錄的是來自對端的TCP選項通告,其成員mss_clamp表示mss的上限值,其來源就是對端的MSS通告,而mss_user是用戶設置的mss,其優先級最高,如果有user_mss,則使用user_mss,忽略其它。
從上面我們可以看到,MSS是可以通過SYN段進行協商的(MSS選項只能出現在SYN報文段中),但它並不是任何條件下都可以協商的,如果一方不接受來自另一方的MSS值,並且沒有user_mss,則MSS就定為默認值536字節(加上首部,允許576字節的IP數據報)。實際上,struct tcp_sock->rx_opt->mss_clamp的初始值就定為536,等收到來自對端的MSS通告后,才進行修改。而結構體struct tcp_sock的成員mss_cache用於緩存上次的有效的mss,其初始值也被定為536。
函數mytcp_sync_mss為一個tcp socket中的mss相關的成員進行數據同步,其基本的一個算法是:
1、當前的MSS正常情況下應該為mtu-IP首部-TCP首部(不包括選項)。
2、struct tcp_sock->rx_opt->mss_clamp中含有對端通告的能夠接受的MSS值,如果該值小於第一步計算所得到的MSS,則以該值為准。
3、IP首部如果帶有IP選項,則MSS中要減去選項長度。
4、如果MSS已經小於48了,則令其等於48。
5、減去TCP首部中選項的長度。
6、如果MSS當前已經大於滑動窗口大小的1/2,則取滑動窗口大小的1/2作為MSS值(但不能小於48)。
7、成員mss_cache用於緩存下剛剛計算所得的MSS。
所以,說本地以太網中MSS為1460的說法並不正確,它還會動態變化,如果IP首部和TCP首部中出現選項,則MSS要相應的減小,一般TCP首部中會有12字節的時間戳選項(外加兩字節的填充選項),這時的MSS就等於1448。
MSS的主要作用是限制另一端主機發送的數據的長度,同時,主機本身也控制自己發送數據報的長度,這將使以較小MTU連接到一個網絡上的主機避免分段。
struct tcp_sock有一個成員xmit_size_goal,用於記錄該socket發送數據報時的segment的大小,一般情況下它的值就等於MSS(特殊情況有例外,以后再分析)。
----------------------------------------
以太網(IEEE 802.3)幀格式:
1、前導碼:7字節0x55,一串1、0間隔,用於信號同步
2、幀起始定界符:1字節0xD5(10101011),表示一幀開始
3、DA(目的MAC):6字節
4、SA(源MAC):6字節
5、類型/長度:2字節,0~1500保留為長度域值,1536~65535保留為類型域值(0x0600~0xFFFF)
6、數據:46~1500字節
7、幀校驗序列(FCS):4字節,使用CRC計算從目的MAC到數據域這部分內容而得到的校驗和。
以CSMA/CD作為MAC算法的一類LAN稱為以太網。CSMA/CD沖突避免的方法:先聽后發、邊聽邊發、隨機延遲后重發。一旦發生沖突,必須讓每台主機都能檢測到。關於最小發送間隙和最小幀長的規定也是為了避免沖突。
考慮如下的情況,主機發送的幀很小,而兩台沖突主機相距很遠。在主機A發送的幀傳輸到B的前一刻,B開始發送幀。這樣,當A的幀到達B時,B檢測到沖突,於是發送沖突信號。假如在B的沖突信號傳輸到A之前,A的幀已經發送完畢,那么A將檢測不到沖突而誤認為已發送成功。由於信號傳播是有時延的,因此檢測沖突也需要一定的時間。這也是為什么必須有個最小幀長的限制。
按照標准,10Mbps以太網采用中繼器時,連接的最大長度是2500米,最多經過4個中繼器,因此規定對10Mbps以太網一幀的最小發送時間為51.2微秒。這段時間所能傳輸的數據為512位,因此也稱該時間為512位時。這個時間定義為以太網時隙,或沖突時槽。512位=64字節,這就是以太網幀最小64字節的原因。
512位時是主機捕獲信道的時間。如果某主機發送一個幀的64字節仍無沖突,以后也就不會再發生沖突了,稱此主機捕獲了信道。
由於信道是所有主機共享的,如果數據幀太長就會出現有的主機長時間不能發送數據,而且有的發送數據可能超出接收端的緩沖區大小,造成緩沖溢出。為避免單一主機占用信道時間過長,規定了以太網幀的最大幀長為1500。
100Mbps以太網的時隙仍為512位時,以太網規定一幀的最小發送時間必須為5.12μs。
1000Mbps以太網的時隙增至512字節,即4096位時,4.096μs。
*************************************MTU的含義: MAC幀內的數據(Payload)字段的最大長度
我們使用Ping命令時, -l參數所指定的數據包大小, 是指的ICMP報文中的ICMPData字段的長度,不包括ICMP Header,更不包括IPHeader.
以太網封裝IP數據包的最大長度是1500字節,也就是說以太網最大幀長應該是以太網首部加上1500,再加上7字節的前導同步碼和1字節的幀開始定界符,具體就是:7字節前導同步嗎+1字節幀開始定界符+6字節的目的MAC+6字節的源MAC+2字節的幀類型+1500+4字節的FCS。