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