1、概述 首先要看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}}}
不同的協(xié)議層對數(shù)據(jù)包有不同的稱謂,在傳輸層叫做段(segment),在網(wǎng)絡(luò)層叫做數(shù)據(jù)報(datagram),在鏈路層叫做幀(frame)。數(shù)據(jù)封裝成幀后發(fā)到傳輸介質(zhì)上,到達(dá)目的主機(jī)后每層協(xié)議再剝掉相應(yīng)的首部,最后將應(yīng)用層數(shù)據(jù)交給應(yīng)用程序處理。 在應(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ò)層,因?yàn)镮P包的首部要占用20字節(jié),所以這的MTU為1500-20=1480; 3.在傳輸層,對于UDP包的首部要占用8字節(jié),所以這的MTU為1480-8=1472; 所以,在應(yīng)用層,你的Data最大長度為1472。當(dāng)我們的UDP包中的數(shù)據(jù)多于MTU(1472)時,發(fā)送方的IP層需要分片fragmentation進(jìn)行傳輸,而在接收方IP層則需要進(jìn)行數(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è)置成不同的值(小于默認(rèn)值),Internet上的標(biāo)準(zhǔn)MTU值為576,所以Internet的UDP編程時數(shù)據(jù)長度最好在576-20-8=548字節(jié)以內(nèi)。 2、TCP、UDP數(shù)據(jù)包最大值的確定 UDP和TCP協(xié)議利用端口號實(shí)現(xiàn)多項(xiàng)應(yīng)用同時發(fā)送和接收數(shù)據(jù)。數(shù)據(jù)通過源端口發(fā)送出去,通過目標(biāo)端口接收。有的網(wǎng)絡(luò)應(yīng)用只能使用預(yù)留或注冊的靜態(tài)端口;而另外一些網(wǎng)絡(luò)應(yīng)用則可以使用未被注冊的動態(tài)端口。因?yàn)閁DP和TCP報頭使用兩個字節(jié)存放端口號,所以端口號的有效范圍是從0到65535。動態(tài)端口的范圍是從1024到65535?! ?/p> MTU最大傳輸單元,這個最大傳輸單元實(shí)際上和鏈路層協(xié)議有著密切的關(guān)系,EthernetII幀的結(jié)構(gòu)DMAC+SMAC+Type+Data+CRC由于以太網(wǎng)傳輸電氣方面的限制,每個以太網(wǎng)幀都有最小的大小64Bytes最大不能超過1518Bytes,對于小于或者大于這個限制的以太網(wǎng)幀我們都可以視之為錯誤的數(shù)據(jù)幀,一般的以太網(wǎng)轉(zhuǎn)發(fā)設(shè)備會丟棄這些數(shù)據(jù)幀。 由于以太網(wǎng)EthernetII最大的數(shù)據(jù)幀是1518Bytes這樣,刨去以太網(wǎng)幀的幀頭(DMAC目的MAC地址48bits=6Bytes+SMAC源MAC地址48bits=6Bytes+Type域2Bytes)14Bytes和幀尾CRC校驗(yàn)部分4Bytes那么剩下承載上層協(xié)議的地方也就是Data域最大就只能有1500Bytes這個值我們就把它稱之為MTU。 UDP 包的大小就應(yīng)該是 1500 - IP頭(20) - UDP頭(8) = 1472(Bytes) TCP 包的大小就應(yīng)該是 1500 - IP頭(20) - TCP頭(20) = 1460 (Bytes) 注*PPPoE所謂PPPoE就是在以太網(wǎng)上面跑“PPP”。隨著寬帶接入(這種寬帶接入一般為Cable Modem或者xDSL或者以太網(wǎng)的接入),因?yàn)橐蕴W(wǎng)缺乏認(rèn)證計費(fèi)機(jī)制而傳統(tǒng)運(yùn)營商是通過PPP協(xié)議來對撥號等接入服務(wù)進(jìn)行認(rèn)證計費(fèi)的,所以引入PPPoE。PPPoE導(dǎo)致MTU變小了以太網(wǎng)的MTU是1500,再減去PPP的包頭包尾的開銷(8Bytes),就變成1492。不過目前大多數(shù)的路由設(shè)備的MTU都為1500。 如果我們定義的TCP和UDP包沒有超過范圍,那么我們的包在IP層就不用分包了,這樣傳輸過程中就避免了在IP層組包發(fā)生的錯誤;如果超過范圍,既IP數(shù)據(jù)報大于1500字節(jié),發(fā)送方IP層就需要將數(shù)據(jù)包分成若干片,而接收方IP層就需要進(jìn)行數(shù)據(jù)報的重組。更嚴(yán)重的是,如果使用UDP協(xié)議,當(dāng)IP層組包發(fā)生錯誤,那么包就會被丟棄。接收方無法重組數(shù)據(jù)報,將導(dǎo)致丟棄整個IP數(shù)據(jù)報。UDP不保證可靠傳輸;但是TCP發(fā)生組包錯誤時,該包會被重傳,保證可靠傳輸。 UDP數(shù)據(jù)報的長度是指包括報頭和數(shù)據(jù)部分在內(nèi)的總字節(jié)數(shù),其中報頭長度固定,數(shù)據(jù)部分可變。數(shù)據(jù)報的最大長度根據(jù)操作環(huán)境的不同而各異。從理論上說,包含報頭在內(nèi)的數(shù)據(jù)報的最大長度為65535字節(jié)(64K)。 我們在用Socket編程時,UDP協(xié)議要求包小于64K。TCP沒有限定,TCP包頭中就沒有“包長度”字段,而完全依靠IP層去處理分幀。這就是為什么TCP常常被稱作一種“流協(xié)議”的原因,開發(fā)者在使用TCP服務(wù)的時候,不必去關(guān)心數(shù)據(jù)包的大小,只需講SOCKET看作一條數(shù)據(jù)流的入口,往里面放數(shù)據(jù)就是了,TCP協(xié)議本身會進(jìn)行擁塞/流量控制。 不過鑒于Internet(非局域網(wǎng))上的標(biāo)準(zhǔn)MTU值為576字節(jié),所以建議在進(jìn)行Internet的UDP編程時,最好將UDP的數(shù)據(jù)長度控制在548字節(jié) (576-8-20)以內(nèi)。 3、TCP、UDP數(shù)據(jù)包最小值的確定 在用UDP局域網(wǎng)通信時,經(jīng)常發(fā)生“Hello World”來進(jìn)行測試,但是“Hello World”并不滿足最小有效數(shù)據(jù)(64-46)的要求,為什么小于18個字節(jié),對方仍然可用收到呢?因?yàn)樵阪溌穼拥腗AC子層中會進(jìn)行數(shù)據(jù)補(bǔ)齊,不足18個字節(jié)的用0補(bǔ)齊。但當(dāng)服務(wù)器在公網(wǎng),客戶端在內(nèi)網(wǎng),發(fā)生小于18個字節(jié)的數(shù)據(jù),就會出現(xiàn)接收端收不到數(shù)據(jù)的情況。 以太網(wǎng)EthernetII規(guī)定,以太網(wǎng)幀數(shù)據(jù)域部分最小為46字節(jié),也就是以太網(wǎng)幀最小是6+6+2+46+4=64。除去4個字節(jié)的FCS,因此,抓包時就是60字節(jié)。當(dāng)數(shù)據(jù)字段的長度小于46字節(jié)時,MAC子層就會在數(shù)據(jù)字段的后面填充以滿足數(shù)據(jù)幀長不小于64字節(jié)。由于填充數(shù)據(jù)是由MAC子層負(fù)責(zé),也就是設(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確認(rèn)幀的大小一個是54字節(jié),一個是60字節(jié),wireshark抓取時沒有填充數(shù)據(jù)段,sniffer抓取時有填充數(shù)據(jù)段。 4、實(shí)際應(yīng)用 用UDP協(xié)議發(fā)送時,用sendto函數(shù)最大能發(fā)送數(shù)據(jù)的長度為:65535- IP頭(20) - UDP頭(8)=65507字節(jié)。用sendto函數(shù)發(fā)送數(shù)據(jù)時,如果發(fā)送數(shù)據(jù)長度大于該值,則函數(shù)會返回錯誤。 用TCP協(xié)議發(fā)送時,由于TCP是數(shù)據(jù)流協(xié)議,因此不存在包大小的限制(暫不考慮緩沖區(qū)的大?。@是指在用send函數(shù)時,數(shù)據(jù)長度參數(shù)不受限制。而實(shí)際上,所指定的這段數(shù)據(jù)并不一定會一次性發(fā)送出去,如果這段數(shù)據(jù)比較長,會被分段發(fā)送,如果比較短,可能會等待和下一次數(shù)據(jù)一起發(fā)送。 |
|