1. 程式人生 > >第二章 傳輸層:TCP、UDP和SCTP

第二章 傳輸層:TCP、UDP和SCTP

發送 主機 溢出 封裝 首部 序列號 設計 sig 錯誤

//1.
IPv4 : 網際協議版本4。使用32位地址。IPv4給TCP、UDP、SCTP、ICMP、IGMP提供分組遞送服務。
IPv6 : 網際協議版本6。使用128位地址。IPv6給TCP、UDP、SCTP、ICMPv6提供分組遞送服務。

TCP/IP協議概況:
技術分享
IP協議:
技術分享
//2.
UDP簡介:
UDP是一個簡單的傳輸層協議,應用進程往一個UDP套接字寫入一個消息,該消息隨後被封裝到一個UDP數據報中,
該UDP數據包又被封裝入一個IP數據報,然後發送至目的地。UDP不保證其數據報會到達其最終目的地,
不保證各個數據報的先後順序跨網路後保持不變,也不保證每個數據報只到達一次。
如果一個數據報到達了最終目的地,但是校驗和校驗發現有錯誤,或者該數據報在傳輸途中被丟失,他就無法被投遞給套接字,
也不會被源端重傳,若想保證一個數據報到達其目的地,需要添加:來自對端的確認、本端的超時重傳等特性。
每個UDP數據報都有一個長度,如果一個數據報正確地到達其目的地,那麽該數據報的長度將隨數據一起傳遞給接收端程序。
相對應的TCP是一個字節流協議,沒有任何的記錄邊界。
我們也說UDP提供無連接的服務,因為UDP客戶與服務器之間不必存在任何長期的關系,一個UDP客戶可以創建一個套接字並發送
一個數據報給一個給定的服務器,然後立即用同一個套接字發送另一個數據報給另一個服務器。同樣的,一個UDP服務器可以用
同一個UDP套接字從若幹個不同的客戶接收數據報。

UDP協議:
技術分享
//3.
TCP簡介:
TCP提供客戶與服務器之間的連接。TCP提供了可靠性,當TCP向另一端發送數據時,他要求對端返回一個確認。
如果沒有收到確認,TCP就自動重傳數據並等待更長時間,當多次重傳失敗後,TCP才放棄,如此在嘗試發送數據上所花費的時間一般為4-10分鐘(取決於實現)
TCP含有動態估算服務器和客戶之間的往返時間的算法,以便他知道等待一個確認需要多少時間。

TCP通過給其中每個字節關聯一個序列號對所發送的數據進行排序。舉例:
假設一個應用寫2048字節到一個TCP套接字,導致TCP發送2個分節(TCP傳遞給IP的數據單元):第一個分節所含數據序列號1-1024,
第二個分節所含數據的分節號1024-2048。如果這些分節非順序到達,接收端TCP將先根據他們的序列號進行排序,再把結果數據傳遞給接收應用。
如果接收端TCP收到來自對端的重復數據,他可以根據序列號判定數據是重復的,從而丟棄數據。

TCP提供流量控制,TCP總是告知對端在任何時刻他一次能夠從對端接受多少字節的數據,稱為通告窗口。在任何時刻,該窗口指出接收緩沖區當前可用的空間量,
從而確保發送端發送的數據不會使接收緩沖區溢出。該窗口時刻動態變化:當接收到來自對端的數據時,窗口大小減小,當接收端應用從緩沖區中讀取數據時,
該窗口就增大。通告窗口大小減小到0是有可能的:當TCP對應某個套接字的接收緩沖區已滿,導致它必須等待應用從該緩沖區讀取數據時,方能從對端再接收數據。

TCP連接是全雙工的,這意味著在一個給定的連接上,應用可以在任何時刻在進出兩個方向上既發送數據又接收數據。
TCP必須為每個數據流方向跟蹤諸如序列號和通告窗口大小等狀態信息。建立一個全雙工連接後,需要的話可以把他轉為一個單雙工連接。
UDP可以是全雙工的。

TCP協議:
技術分享
//4.
TCP建立連接過程:
A:服務器必須準備好接受外來的連接。這通常通過調用socket、bind、listen這3個函數來完成。稱之為被動打開。
B:客戶通過調用connect發起主動連接。這導致客戶TCP發送一個SYN(同步)分節,它告訴服務器客戶將在待建立的連接中發送的數據的初始序列號。
  通常SYN分節不攜帶數據,其所在的IP報只含有一個IP首部、一個TCP首部及可能有的TCP選項
C:服務器必須確認(ACK)客戶的SYN,同時也得發送一個SYN分節,含有服務器將在同一連接中發送的數據的初始序列號。服務器在單個分節中發送SYN和對客戶SYN的ACK。
D:客戶必須確認服務器的SYN。
以上交換至少需要三個分組,因此稱為TCP的三路握手。

TCP的三路握手:
技術分享
//5.
每一個SYN可以含有多個TCP選項,下面是一些常用的TCP選項:
(1):MSS選項:發送SYN的TCP一端使用本選項通告對端他的最大分節大小,即MSS,也就是他在本連接的每個TCP分節中願意接受的最大數據量(不包括2層協議頭)大小。
		TCP_MAXSEG 套接字選項可以提取與設置這個TCP選項
(2):窗口規模選項:TCP連接任何一端能夠通告對端的最大窗口大小是65535(TCP協議裏窗口大小占16bit),窗口規模選項指定TCP首部中窗口必須左移位數,取值範圍:0-14
	備註:由上可知,TCP窗口規模最大為65535*16384,即接近1GB,但是通過代碼設置TCP接收和發送緩沖區大於1GB時,仍然成功,原因待考慮

種類		長度			名稱			描述
0		1			EOL			選項列表結束
1		1			NOP			無操作,用於填充
2		4			MSS			最大段大小
3		3			WSOPT			窗口縮放因子
4		2			SACK_Permitted		發送者支持SACK選項
5		可變			SACK			SACK阻塞(接收到亂序數據)
8		10			TSOPT			時間戳選項
28		4			UTO			用戶超時
29		可變			TCP-AO			認證選項
253		可變			Experimental		保留
254		可變			Experimental		保留	


//6.
TCP連接終止:
TCP建立一個連接需要三個分節,終止一個連接需要4個分節
A:某個應用進程首先調用close,我們稱該端執行主動關閉。該端的TCP於是發送一個FIN分節,表示數據發送完畢
B:接收到這個FIN的對端執行被動關閉。這個FIN由TCP確認,他的接收作為文件結束符傳遞給接收端應用進程(放在以排隊等候該應用程序接收的其他數據之後),
  FIN的接收意味著接收端應用進程在相應連接上再無額外數據可接收
C:一段時間後,接收到這個FIN的應用進程將調用close關閉他的套接字。這導致他的TCP也發送一個FIN
D:接收這個最終FIN的原發送端確認這個FIN
以上,每個方向需要發送一個FIN和一個ACK,因此通常需要4個分節,但是有些情況步驟一的FIN會隨數據一起發送,步驟二和步驟三也可能被合並發送。
在步驟二和步驟三之間,從執行被動關閉到執行主動關閉一端流動數據是有可能的,這稱為半關閉

TCP連接關閉時的分組交換
技術分享
//7.
TCP為一個連接定義了11種狀態,這些狀態可由netstat顯示

TCP狀態轉換圖
技術分享
TCP連接的分組交換
技術分享
//8.
TIME_WAIT狀態:
停留在這個狀態的持續時間是最大分節生命期(maximum segment lifetime, MSL)的兩倍,稱之為2MSL
任何TCP實現都必須為MSL選擇一個值,RFC1122的建議是2分鐘。
MSL是任何IP數據報能夠在因特網上存活的最長時間

TIME_WAIT的存在理由:
(1):可靠的實現TCP全雙工連接的終止
(2):允許老的重復分節在網絡中消逝
詳解:
客戶端發起的關閉時客戶端和服務器的狀態轉換圖:
技術分享
具體過程如下:
A:客戶端發送FIN報文段,進入FIN_WAIT_1狀態。
B:服務器端收到FIN報文段,發送ACK表示確認,進入CLOSE_WAIT狀態。
C:客戶端收到FIN的確認報文段,進入FIN_WAIT_2狀態。
D:服務器端發送FIN報文端,進入LAST_ACK狀態。
E:客戶端收到FIN報文端,發送FIN的ACK,同時進入TIME_WAIT狀態,啟動TIME_WAIT定時器,超時時間設為2MSL。
F:服務器端收到FIN的ACK,進入CLOSED狀態。
G:客戶端在2MSL時間內沒收到對端的任何響應,TIME_WAIT超時,進入CLOSED狀態。
從上圖中可以看出,client在發出server端FIN的ACK以後,進入了TIME_WAIT的狀態,直到2MSL以後,才會關閉。
當TCP執行一個主動關閉,並發回最後一個ACK,該連接必須在TIME_WAIT狀態停留的時間為2倍的MSL。這樣可讓TCP再次發送最後的ACK以防這個ACK丟失。
這個規則導致一個後果就是在這個2MSL的時間內,該地址上的連接(客戶端地址、端口和服務器端的地址、端口)不能被使用。
比如我們在建立一個鏈接後關閉鏈接然後迅速重啟鏈接,那麽就會出現端口不可用的情況。
TIME_WAIT狀態的必要性:
客戶端進入發送收到四次握手關閉的最後一個ACK後,進入TIME_WAIT同時發送ACK,如果其不停留2MSL時間,而是馬上關閉連接,銷毀連接上的資源,當發送如下情況時,
將不能正常的完成四次握手關閉:
客戶端發送的ACK在網路上丟失,這樣服務器端收不到最後的ACK,重傳定時器超時,將重傳FIN到客戶端,由於客戶端關於該連接的所有資源都釋放,收到重傳的FIN後,
它沒有關於這個FIN的任何信息,所以向服務器端發送一個RST報文端,服務器端收到RST後,認為搞連接出現了異常(而非正常關閉)。
所以,在TIME_WAIT狀態下等待2MSL時間端,是為了能夠正確處理第一個ACK(最長生存時間為MSL)丟失的情況下,能夠收到對端重傳的FIN(最長生存時間為MSL),然後重傳ACK。
是否只要主動關閉方在TIME_WAIT狀態下停留2MSL,四次握手關閉就一定正常完成呢?
答案是否定的?可以考慮如下的情況,
技術分享
TIME_WAIT狀態下發送的ACK丟失,LAST_ACK時刻設定的重傳定時器超時,發送重傳的FIN,很不幸,這個FIN也丟失,主動關閉方在TIME_WAIT狀態等待2MSL沒收到任何報文段,
進入CLOSED狀態,當此時被動關閉方並沒有收到最後的ACK。所以即使要主動關閉方在TIME_WAIT狀態下停留2MSL,也不一定表示四次握手關閉就一定正常完成。
結論:在TIME_WAIT下等待2MSL,只是為了盡最大努力保證四次握手正常關閉。確保老的報文段在網絡中消失,不會影響新建立的連接
考慮如下的情況,主動關閉方在TIME_WAIT狀態下發送的ACK由於網絡延遲的原因沒有按時到底(但並沒有超過MSL的時間),導致被動關閉方重傳FIN,在FIN重傳後,
延遲的ACK到達,被動關閉方進入CLOSED狀態,如果主動關閉方在TIME_WAIT狀態下發送ACK後馬上進入CLOSED狀態(也就是沒有等待)2MSL時間,則上述的連接已不存在:
現在考慮下面的情況,假設上述的鏈接的四元組為(192.201.0.80:23,192.201.0.85:5555),由於連接已關閉,我們可以馬上建立一個新的連接,
並且這個連接的四元組也是(192.201.0.80:23,192.201.0.85:5555),那麽當上一個連接的重傳FIN到達主動關閉方時,被新的連接所接受,這將導致新的連接被復位,很顯然,這不是我們希望看到的事情。
新的連接要建立,必須是在主動關閉方和被動關閉方都進入到CLOSED狀態之後才有可能。所以,最有可能導致舊的報文段影響新的連接的情況是:
在TIME_WAIT狀態之前,主動關閉方發送的報文端在網絡中延遲,但是TIME_WAIT設定為2MSL時,這些報文端必然會在網絡中消失(最大生存時間為MSL)。
被動關閉方最有可能影響新連接的報文段就是我們上面討論的情況,對方ACK延遲到達,在此之前重傳的FIN,這個報文端發送之後,TIME_WAIT的定時器超時時間肯定大於MSL,
在1MSL時間內,這個FIN要麽在網絡中因為生成時間到達而消失,要麽到達主動關閉方被這確的處理,不會影響新建立的連接。


//9.
任何時候,多個進程可能同時使用TCP、UDP或者SCTP這三種傳輸層協議中的任何一種,這三種協議都使用16位整數來區分這些進程
IANA(the Internet Assigned Number Authority,因特網已分配數值權威機構)維護著一個端口號分配的清單,端口號被分為3段:
(1):眾所周知的端口:0-1023 這些端口由IANA分配和控制
(2):已登記的端口:1024-49151 這些端口不受IANA控制,不過由IANA登記並提供他們的使用情況的清單
(3):臨時端口:49152-65535 IANA並不管這些端口


//10.
一個TCP連接的套接字對是定義該連接的兩個端點的四元組:本地Ip,本地Port,外地Ip,外地Port
套接字對唯一標示一個網絡上的每個TCP連接

當服務器監聽一個Ip端口,多個客戶端同時連接這個Ip端口。至此,我們必須在服務器主機上區分監聽套接字和已連接套接字,這兩種套接字都使用相同的端口,即服務器bind時候使用的端口
TCP無法僅僅通過查看目的端口號來分離外來的分節到不同的端點,他必須查看套接字的所有四個元素才能確定由哪個端點接收某個到達的分節

多個客戶端與同一個服務器連接
技術分享
//11.
影響IP數據報大小的限制:
A:IPv4數據報的最大大小是65535字節,包括IPv4的首部,因為其總長度字段占據16位
B:IPv6數據報的最大大小是65575字節,包括40字節首部,這是因為其凈荷長度字段占據16位。註意:其凈荷長度字段不包括IPv6首部,而IPv4的總長度字段包括IPv4首部
  IPv6有一個特大凈荷選項,可以將凈荷長度擴展為32位,不過這個選項需要MTU查過65535的數據鏈路提供支持(這是為主機到主機內部連接而設計的,比如HIPPI,他們通常沒有內在的MTU)
C:許多網絡有一個可由硬件規定的MTU(maximum transmiss unit,最大傳輸單元)。以太網的MTU是1500字節
D:在兩個主機之間的路徑中最小的MTU稱為路徑MTU。兩個主機之間相反方向上的MTU可以不一致,因為因特網中路由選擇往往是不對稱的。以太網的1500字節MTU是當今常見的路徑MTU
E:當一個IP數據報將從某個接口送出時,如果他的大小超過相應鏈路的MTU,IPv4和IPv6都將執行分片。這些片段在到達最終目的地前通常不會被重組 (備註:如何確保分片的數據的可靠性,待研究)
  IPv4主機對其產生的數據報執行分片,IPv4路由器則對其轉發的數據報執行分片
  IPv6只有主機對其產生的數據報進行分片,IPv6路由器(通常)不對其轉發的數據報執行分片
F:IPv4首部的不分片(DF位)若被設置,那麽不管是發送這些數據報的主機還是轉發數據報的路由器,都不允許對他們進行分片,當路由器接收到一個超過其外出鏈路MTU大小且設置了DF位的IPv4報,
  他將產生一個ICMPv4(目的地不可達,需分片但DF位已設置)出錯消息
G:最小重組緩沖區大小是IPv4和IPv6的任何實現都必須支持的最小數據包大小,其值對於IPv4為576字節,對於IPv6為1500字節。
  例如:就IPv4來說,我們不能判定某個給定的目的地能否接收577字節的數據報,為此使用UDP的IPv4網絡應用(如DNS、RIP、TFTP、BOOTP、SNMP)應該避免產生大於這個大小的數據報
H:TCP的MSS(最大分節大小):用於向對端TCP通告對端在每個分節能發送的最大TCP數據量。MSS經常設置為MTU減去IP和TCP首部的固定長度,在以太網中使用IPv4的MSS值為1460,使用IPv6的MSS是1440
  對具有特大凈荷選項的IPv6,65535這個MSS值被視為無限的一個特殊值
  MSS選項的目的之一就是避免分片


//12.
每一個TCP套接字都有一個發送緩沖區,可以使用 SO_SNDBUF 套接字選項來更改該緩沖區大小
當某個進程調用write時,內核從該應用進程的緩沖區中復制所有數據到所寫套接字的發送緩沖區,如果該套接字的發送緩沖區容不下這麽多數據,該進程會被投入睡眠(阻塞情況下)
從寫一個TCP套接字的write調用成功,僅僅表示我們可以使用原來的應用進程緩沖區,並不表示對端的TCP已接收到數據
對端TCP必須確認收到的數據,伴隨來自對端的ACK不斷到達,本端TCP至此才能從套接字發送緩沖區丟棄已確認的數據,TCP必須為已發送的數據保留一個副本,直到他被對端確認為止

進程寫TCP套接字時涉及的緩沖區和步驟
技術分享
//13.
任何UDP套接字都有發送緩沖區大小,可以使用 SO_SNDBUF 套接字選項去更改他,不過其對應的發送緩沖區實際上並不存在,這個發送緩沖區大小僅僅是可以寫到該套接字的UDP數據報的大小上限
如果進程寫一個大於套接字發送緩沖區大小的數據報,內核將返回一個 EMSGSIZE 錯誤。既然UDP是不可靠的,他就不必保存進程數據的副本,因此無需一個真正的發送緩沖區
應用進程的發送的數據在沿協議棧向下傳遞時,通常被復制到某種格式的一個內核緩沖區,然而當該數據被發送後,這個副本就被數據鏈路層丟棄了
從寫一個UDP套接字的write調用成功返回表示所寫的數據報或其片段已被加入數據鏈路層的輸出隊列。如果該隊列沒有足夠的空間存放該數據報或其某個片段,內核通常返回一個 ENOBUFS 錯誤
註意:有些UDP的實現不返回這種錯誤,這樣甚至數據報未經發送就被丟棄的情況,進程也是不知道的

進程寫UDP套接字時涉及的緩沖區與步驟
技術分享


  

第二章 傳輸層:TCP、UDP和SCTP