這個問題在前面有的部分已經涉及,這里在重新總結下。主要參考UNIX網絡編程。
(1)數據報大小
IPv4的數據報最大大小是65535字節,包括IPv4首部。因為首部中說明大小的字段為16位。
IPv6的數據報最大大小是65575字節,包括40字節的IPv6首部。同樣是展16位,但是IPv6首部大小不算在里面,所以總大小比IPv4大一個首部(40字節)。
(2)MTU
許多網絡有一個可由硬件規定的MTU。以太網的MTU為1500字節。有一些鏈路的MTU的MTU可以由認為配置。IPv4要求的最小鏈路MTU為68字節。這允許最大的IPv4首部(包括20字節的固定長度部分和最多40字節的選項部分)拼接最小的片段(IPv4首部中片段偏移字段以8個字節為單位)IPv6要求的最小鏈路MTU為1280字節。
(3)分片(fragmentation)
當一個IP數據報從某個接口送出時,如果它的大小超過相應鏈路的MTU,IPv4和IPv6都將執行分片。這些片段在到達終點之前通常不會被重組(reassembling)。IPv4主機對其產生的數據報執行分片,IPv4路由器則對其轉發的數據報進行分片。然后IPv6只有主機對其產生的數據報執行分片,IPv6路由器不對其轉發的數據報執行分片。
IPv4首部的“不分片”(do not fragment)位(即DF位)若被設置,那么不管是發送這些數據報的主機還是轉發他們的路由器,都不允許對它們分片。當路由器接收到一個超過其外出鏈路MTU大小且設置了DF位的IPv4數據報時,它將產生一個ICMPv4“destination unreachable,fragmentation needed but DF bit set”(目的不可到達,需分片但DF位已設置)的出錯消息。
既然IPv6路由器不執行分片,每個IPv6數據報於是隱含一個DF位。當IPv6路由器接收到一個超過其外出鏈路MTU大小的IPv6數據報時,它將產生一個ICMPv6 “packet too big”的出錯消息。IPv4的DF位和隱含DF位可用於路徑MTU發現。
(4)最小重組緩沖區大小(minimum reassembly buffer size)
IPv4和IPv6都定義了最小緩沖區大小,它是IPv4或IPv6任何實現都必須保重支持的最小數據報大小。其值對IPv4為576字節,對於IPv6為1500字節。例如,對於IPv4而言,我們不能判定某個給定的目的能否接受577字節的數據報,為此很多應用避免產生大於這個大小的數據報。
(5)MSS(maximun segment size)
TCP有一個最大分段大小,用於對端TCP通告對端每個分段中能發送的最大TCP數據量。MSS的目的是告訴對端其重組緩沖區大小的實際值,從而避免分片。MSS經常設計成MTU減去IP和TCP首部的固定長度。以太網中使用IPv4MSS值為1460,使用IPv6的MSS值為1440(兩者TCP首部都是20字節,但是IPv6首部是40字節,IPv4首部是20字節)。
(6)TCP發送緩沖區
每個TCP套接字有一個發送緩沖區,我們可以用SO_SNDBUF套接字選項來更改該緩沖區的大小。當某個應用進程調用write時,內核從該應用進程的緩沖區復制所有數據到縮寫套接字的發送緩沖區。如果該套接字的發送緩沖區容不下該應用進程的所有數據(或是應用進程的緩沖區大於套接字的發送緩沖區,或是套接字的發送緩沖區中已有其他數據),該應用進程將被投入睡眠。這里假設該套接字是阻塞的,它通常是默認設置。內核將不從write系統調用返回,直到應用進程緩沖區中的所有數據都復制到套接字發送緩沖區。因此,從寫一個TCP套接字的write調用成功返回僅僅表示我們可以重新使用原來的應用進程緩沖區,並不表明對端的TCP或應用進程已接受到數據。
這一端的TCP提取套接字發送緩沖區中的數據並把它發送給對端的TCP,其過程基於TCP數據傳送的所有規則。對端TCP必須確認收到的數據,伴隨來自對端的ACK的不斷到達,本段TCP至此才能從套接字發送緩沖區中丟棄已確認的數據。TCP必須為已發送的數據保留一個副本,直到它被對端確認為止。本端TCP以MSS大小或是更小的塊把數據傳遞給IP,同時給每個數據塊安上一個TCP首部以構成TCP分節,其中MSS或是由對端告知的值,或是536(若未發送一個MSS選項為576-TCP首部-IP首部)。IP給每個TCP分節安上一個IP首部以構成IP數據報,並按照其目的的IP地址查找路由表項以確定外出接口,然后把數據報傳遞給相應的數據鏈路。每個數據鏈路都有一個數據隊列,如果該隊列已滿,那么新到的分組將被丟棄,並沿協議棧向上返回一個錯誤:從數據鏈路到IP,在從IP到TCP。TCP將注意到這個錯誤,並在以后某個時候重傳相應的分節。應用程序不知道這種暫時的情況。
(7)UDP發送緩沖區
任何UDP套接字都有發送緩沖區大小(我們可以用SO_SNDBUF套接字選項更改它),不過它僅僅是可寫道套接字UDP數據報大小上限。如果一個應用進程寫一個大於套接字發送緩沖區大小的數據報,內核將返回該進程一個EMSGSIZE錯誤。既然UDP是不可靠的,它不必保存應用進程數據的一個副本,因此無需一個真正的發送緩沖區。(應用進程的數據在沿協議棧向下傳遞時,通常被復制到某種格式的一個內核緩沖區中,然而當該數據被發送之后,這個副本被數據鏈路層丟棄了。)
UDP簡單地給來自用戶的數據報安上8字節首部以構成UDP數據報,然后傳遞給IP。IPv4或IPv6給UDP數據報安上相應的IP首部以構成IP數據報,執行路由操作確定外出接口,然后或者直接把數據報加入數據鏈路層輸出隊列(如果適合於MTU),或者分片后在把每個片段加入數據集鏈路層的輸出隊列。如果某個UDP進程發送大數據報,那么它們相比TCP應用數據更有可能被分片,因為TCP會把應用數據划分成MSS大小的塊,而UDP卻沒有對等的手段。
從寫一個UDP套接字的write調用成功返回表示所寫的數據報或其所有片段已被加入數據鏈路層的輸出隊列。如果該隊列沒有足夠的空間存放該數據報或它的某個片段,內核通常會返回一個ENOBUFS錯誤給它的應用進程。有些UDP實現不返回這種錯誤,這樣甚至數據報未經發送就被丟棄的情況進程也不知道。