章節回顧:
《TCP/IP詳解卷1:協議》第3章 IP:網際協議(1)-讀書筆記
《TCP/IP詳解卷1:協議》第3章 IP:網際協議(2)-讀書筆記
《TCP/IP詳解卷1:協議》第4章 ARP:地址解析協議-讀書筆記
《TCP/IP詳解卷1:協議》第5章 RARP:逆地址解析協議-讀書筆記
《TCP/IP詳解卷1:協議》第6章 ICMP:Internet控制報文協議-讀書筆記
《TCP/IP詳解卷1:協議》第11章 UDP:使用者資料包協議-讀書筆記
《TCP/IP詳解卷1:協議》第17、18章 TCP:傳輸控制協議(1)-讀書筆記
《TCP/IP詳解卷1:協議》第17、18章 TCP:傳輸控制協議(2)-讀書筆記
《TCP/IP詳解卷1:協議》第19章 TCP的互動資料流-讀書筆記
3、連線建立的超時
有很多情況導致無法建立連線。一種情況是伺服器主機沒有處於正常狀態。
4、最大報文段長度
最大報文段長度(MSS)表示TCP傳往另一端的最大塊資料的長度。當一個連線建立時,連線的雙方都要通告各自的MSS。
當建立一個連線時,每一方都有用於通告它期望接收的MSS選項(MSS選項只能出現在SYN報文段中)。如果一方不接收來自另一方的MSS值,則MSS就定為預設值536位元組(這個預設值允許20位元組的IP首部和20位元組的TCP首部以適合576位元組IP資料包)。
注意:
(1)一般說來,如果沒有分段發生,MSS越大越好。報文段越大允許每個報文段傳送的資料就越多,相對IP和TCP首部有更高的網路利用率。
(2)當TCP傳送一個SYN時,或者是因為一個本地應用程式想發起一個連線,或者是因為另一端的主機收到了一個連線請求,它能將MSS值設定為外出介面上的MTU長度減去固定的IP首部和TCP首部長度。
(3)如果目的IP地址為“非本地的(non-local)”,MSS通常的預設值為536。
說明:區分地址是本地還是非本地的方法是:如果目的IP地址的網路號與子網號都與本機相同,則是本地的;如果目的IP地址的網路號與本機相同而子網號不同,則可能是本地的,也可能是非本地的。
(4)MSS使得主機限制另一端傳送資料包的長度。加上主機也能控制它傳送資料包的長度,這使得以較小MTU連線到一個網路上的主機避免分段。
下面舉個書中的例子(比較懶,沒有搭建環境)
主機sun向slip發起一個TCP連線,利用tcpdump命令來觀察報文段:
說明:
(1)sun傳送的報文段不能超過256位元組的資料,因為slip已經告知它的MSS值為256。(上圖第二個紅框)
(2)slip知道它外出介面的MTU長度為296,所以即使sun已經告訴它的MSS為1460(上圖第一個紅框),但為避免將資料分段,它不會傳送超過256位元組資料的報文段。
(3)如果兩端主機都連線到乙太網上,都採用536的MSS,但中間網路採用296的MTU,同樣會出現分段。
5、TCP的半關閉
TCP提供了連線的一端在結束它的傳送後還能接收來自另一端資料的能力,即半關閉。
注意:很少有應用程式使用它,如果想要使用這個功能,需要程式設計介面提供一個方式來說明。
下面給出一個例子:
意思是這樣的:客戶結束了傳送資料(傳送了FIN),伺服器傳送ACK表示確認後,仍然可以傳送資料給客戶(圖中紅框)。
6、TCP的狀態變遷圖
下面這張圖我沒有仔細研究,它應該包含了有關發起和終止TCP連線的所有規則。
(1)2MSL等待狀態
TIME_WAIT狀態也稱為2MSL等待狀態。每個具體TCP實現必須選擇一個報文段最大生存時間MSL(Maximum Segment Lifetime)。它是任何報文段被丟棄前在網路內的最長時間。
注意:MSL是個有限的時間,我們知道TCP報文段以IP資料包在網路中傳輸,IP資料包是由TTL欄位限制其生存時間的。RFC 793指出MSL為2分鐘。實現中的常用值是30秒,1分鐘,或2分鐘。
對於給定的MSL值,原則是:當TCP執行一個主動關閉,併發回最後一個ACK,該連線必須在TIME_WAIT狀態停留的時間為2倍的MSL。這樣可讓TCP再次傳送最後的ACK以防這個ACK丟失(另一端超時並重發最後的FIN)。
說明:
1)客戶執行主動關閉並進入TIME_WAIT是正常的。伺服器通常執行被動關閉,不會進入TIME_WAIT狀態。
2)TCP連線在2MSL等待期間,這個連線的socket(客戶的IP地址和埠號,伺服器的IP地址和埠號)不能再被使用。這個連線只能在2MSL結束後才能再被使用。
(2)平靜時間的概念
如果處於2MSL等待埠的主機出現故障,它會在MSL秒內重新啟動,並立即使用故障前處於2MSL的socket來建立一個新的連線。在故障前從這個連線發出而遲到的報文段會被錯誤地當作屬於重啟後新連線的報文段。
為了防止這種情況,RFC 793指出TCP在重啟動後的MSL秒內不能建立任何連線,稱為平靜時間(quiet time)。
(3)FIN_WAIT_2狀態
如上面的圖所示:FIN_WAIT_2狀態時,客戶已經發出了FIN,另一端也已對它進行確認。
除非客戶設定了半關閉,否則將等待另一端的應用層意識到它已收到一個檔案結束符說明,並向我們發一個FIN 來關閉連線。只有這樣,我們這端才會從FIN_WAIT_2狀態進入TIME_WAIT狀態。
7、復位報文段
TCP首部中的RST位元是用於“復位”的,一般無論何時一個報文段發往“基準連線”出現錯誤,TCP都會發出一個復位報文段。
說明:“基準連線”是指由目的IP地址和埠號以及源IP地址和埠號指明的連線。
(1)到不存在的埠的連線請求
產生復位的一種常見情況是當連線請求到達時,目的埠沒有程式正在聽。
注意:對於UDP,當一個資料包到達目的埠時,該埠沒在使用,它將產生一個ICMP埠不可達的資訊;對於TCP,則使用復位。
圖中的意思是說:主機bsdi向svr4的20000埠傳送SYN,然後svr4告訴bsdi一個復位連線的資訊。
(2)異常終止一個連線
終止一個連線的正常方式是一方傳送FIN。這也稱為有序釋放,因為在所有排隊資料都已傳送之後才傳送FIN,正常情況下沒有任何資料丟失。但也有可能傳送一個復位報文段而不是FIN來中途釋放一個連線。這也稱為為異常釋放。
異常終止一個連線對應用程式來說有兩個優點:
1)丟棄任何待發資料並立即傳送復位報文段;
2)RST的接收方會區分另一端執行的是異常關閉還是正常關閉。
特別注意:RST報文段不會導致另一端產生任何響應,另一端根本不進行確認。收到RST的一方將終止該連線,並通知應用層連線復位。
(3)檢測半開啟連線
如果一方已經關閉或異常終止連線而另一方卻還不知道,將這樣的TCP連線稱為半開啟的。
說明:
1)任何一端的主機異常都可能導致半開啟連線。只要不在半開啟連線上傳輸資料,仍處於連線狀態的一方就不會檢測到另一方已經出現異常。
2)半開啟連線的另一個常見原因是當客戶主機突然掉電而不是正常的結束客戶應用程式後再關機。
8、同時開啟
兩個應用程式同時彼此執行主動開啟的情況是可能的,儘管發生的可能性極小。每一方必須傳送一個SYN,且這些SYN必須傳遞給對方。這需要每一方使用一個對方熟知的埠作為本地埠,稱為同時開啟。
TCP對於同時開啟僅建立一條連線而不是兩條連線。當出現同時開啟時:
兩端幾乎同時傳送SYN,並進入SYN_SENT狀態。當每一端收到SYN時,狀態變為SYN_RCVD,同時它們都再發SYN並對收到的SYN進行確認。當雙方都收到SYN及相應的ACK時,狀態都變遷為ESTABLISHED。
注意:
(1)一個同時開啟的連線需要交換4個報文段,比正常的三次握手多一個。
(2)對於同時開啟的連線,我們沒有將任何一端稱為客戶或伺服器,因為每一端既是客戶又是伺服器。
9、同時關閉
雙方都執行主動關閉也是可能的,TCP也允許同時關閉。
同時關閉過程為:
(1)當應用層發出關閉命令時,兩端均從ESTABLISHED變為FIN_WAIT_1。這將導致雙方各傳送一個FIN,兩個FIN經過網路傳送後分別到達另一端。
(2)收到FIN後,狀態由FIN_WAIT_1變為CLOSING,併傳送最後的ACK。
(3)當收到最後的ACK時,狀態變化為TIME_WAIT。
注意:同時關閉和正常關閉報文段交換數目相同。
10、TCP選項
TCP首部可以包含選項部分。
選項說明:
(1)每個選項的開始是1位元組kind欄位,說明選項的型別。
(2)kind欄位為0和1的選項僅佔1個位元組。其他選項在kind位元組後還有len位元組,它說明的長度是指總長度,包括kind位元組和len位元組。
(3)設定無操作選項的原因在於允許發方填充欄位為4位元組的倍數。
11、TCP 伺服器的設計
大多數TCP伺服器程式是併發的。當一個新的連線請求到達伺服器時,伺服器接受這個請求,並呼叫一個新程式來處理這個新的客戶請求。
(1)TCP伺服器埠號
當不同程式連線到伺服器時,伺服器埠號都是一樣的。
(2)限定的本地IP地址
限制本地的IP地址後,不同鏈路的連線請求可能被TCP核心拒絕。
(3)限制遠端IP地址
伺服器必須不指明遠端socket,而等待連線請求的到來,然後檢查客戶端的IP地址和埠號。
圖18-22(這張圖我還沒有看懂)總結了TCP伺服器進行連線時三種型別的地址繫結。在三種情況中,lport是伺服器的熟知埠,local IP必須是一個本地介面的IP地址。
(4)呼入連線請求佇列
一個併發伺服器呼叫一個新的程式來處理每個客戶請求,因此處於被動連線請求的伺服器應該始終準備處理下一個呼入的連線請求。但仍有可能出現當伺服器在建立一個新的程式時,或作業系統正忙於處理優先順序更高的程式時,到達多個連線請求。
在伯克利的TCP實現中採用以下規則:
1)正等待連線請求的一端有一個固定長度的連線佇列,該佇列中的連線已被TCP接受(即三次握手已經完成),但還沒有被應用層所接受。
注意:TCP接受一個連線是將其放入這個佇列,而應用層接受連線是將其從該佇列中移出。
2)應用層將指明該佇列的最大長度,這個值通常稱為積壓值(backlog)。它的取值範圍是0~5之間的整數,包括0和5(大多數的應用程式都將這個值指明為5)。
3)當一個連線請求(SYN)到達時,TCP使用一個演算法,根據當前連線佇列中的連線數來確定是否接收這個連線。
4)如果對於新的連線請求,該TCP監聽的端點的連線佇列中還有空間,TCP模組將對SYN進行確認並完成連線的建立。
注意:
(i)應用層只有在三次握手中的第三個報文段收到後才會知道這個新連線。
(ii)當客戶程式主動開啟成功但伺服器的應用層還不知道這個新的連線時,它可能會認為伺服器程式已經準備好接收資料了。如果這樣,伺服器的TCP僅將接收的資料放入緩衝佇列。
5)如果對於新的連線請求,連線佇列中已沒有空間,TCP將不理會收到的SYN,也不發回任何報文段(即不發回RST)。如果應用層不能及時接受已被TCP接受的連線,這些連線可能佔滿整個連線佇列,客戶的主動開啟最終將超時。