1. 程式人生 > >TCP/IP基礎知識總結

TCP/IP基礎知識總結

此文屬於轉載
先要看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位元組。
******************************************************************************************************************************
    TCP提供的是一種面向連線的,可靠的位元組流服務,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,一串10間隔,用於訊號同步
2、幀起始定界符:1位元組0xD5(10101011),表示一幀開始
3DA(目的MAC)6位元組
4SA(MAC)6位元組
5、型別/長度:2位元組,01500保留為長度域值,153665535保留為型別域值(0x06000xFFFF)
6、資料:461500位元組
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。
按照上述,最大幀應該是1526位元組,但是實際上我們抓包得到的最大幀是1514位元組,為什麼不是1526位元組呢?原因是當資料幀到達網絡卡時,在物理層上網絡卡要先去掉前導同步碼和幀開始定界符,然後對幀進行CRC檢驗,如果幀校驗和錯,就丟棄此幀。如果校驗和正確,就判斷幀的目的硬體地址是否符合自己的接收條件(目的地址是自己的物理硬體地址、廣播地址、可接收的多播硬體地址等),如果符合,就將幀交“裝置驅動程式”做進一步處理。這時我們的抓包軟體才能抓到資料,因此,抓包軟體抓到的是去掉前導同步碼、幀開始分界符、FCS之外的資料,其最大值是6+6+2+1500=1514。
乙太網規定,乙太網幀資料域部分最小為46位元組,也就是乙太網幀最小是6+6+2+46+4=64。除去4個位元組的FCS,因此,抓包時就是60位元組。當資料欄位的長度小於46位元組時,MAC子層就會在資料欄位的後面填充以滿足資料幀長不小於64位元組。由於填充資料是由MAC子層負責,也就是裝置驅動程式。不同的抓包程式和裝置驅動程式所處的優先層次可能不同,抓包程式的優先順序可能比裝置驅動程式更高,也就是說,我們的抓包程式可能在裝置驅動程式還沒有填充不到64位元組的幀的時候,抓包程式已經捕獲了資料。因此不同的抓包工具抓到的資料幀的大小可能不同。下列是本人分別用wireshark和sniffer抓包的結果,對於TCP 的ACK確認幀的大小一個是54位元組,一個是60位元組,wireshark抓取時沒有填充資料段,sniffer抓取時有填充資料段。