Linux C高階程式設計——網路程式設計之TCP(3)
Linux網路程式設計(三)——TCP
宗旨:技術的學習是有限的,分享的精神是無限的。
1、TCP段格式
和UDP協議一樣也有源埠號和目的埠號,通訊的雙方由IP地址和埠號標識。32位序號、32位確認序號、視窗大小。4位首部長度和IP協議頭類似,表示TCP協議頭的長度,以4位元組為單位,因此TCP協議頭最長可以是4x15=60位元組,如果沒有選項欄位, TCP協議頭最短20位元組。URG、 ACK、 PSH、 RST、 SYN、 FIN是六個控制位,本節稍後將解釋SYN、 ACK、 FIN、 RST四個位,其它位的解釋從略。16位檢驗和將TCP協議頭和資料都計算在內。
2、通訊時序——“三次握手,四次揮手”
首先客戶端主動發起連線、傳送請求,然後伺服器端響應請求,然後客戶端主動關閉連線。兩條豎線表示通訊的兩端,從上到下表示時間的先後順序,注意,資料從一端傳到網路的另一端也需要時間,所以圖中的箭頭都是斜的。雙方傳送的段按時間順序編號為1-10,各段中的主要資訊在箭頭上標出,例如段2的箭頭上標著SYN, 8000(0), ACK 1001, <mss 1024>,表示該段中的SYN位置1,32位序號是8000,該段不攜帶有效載荷(資料位元組數為0),ACK位置1,32位確認序號是1001,帶有一個mss選項值為1024。
建立連線的過程:
1. 客戶端發出段1, SYN位表示連線請求。序號是1000,這個序號在網路通訊中用作臨時的地
2. 伺服器發出段2,也帶有SYN位,同時置ACK位表示確認,確認序號是1001,表示“我接收到序號1000及其以前所有的段,請你下次傳送序號為1001的段”,也就是應答了客戶端的連線請求,同時也給客戶端發出一個連線請求,同時宣告最大尺寸為
3. 客戶端發出段3,對伺服器的連線請求進行應答,確認序號是8001。
客戶端和伺服器分別給對方發了連線請求,也應答了對方的連線請求,其中伺服器的請求和應答在一個段中發出,因此一共有三個段用於建立連線,稱為'''三方握手( three-wayhandshake) '''。在建立連線的同時,雙方協商了一些資訊,例如雙方傳送序號的初始值、最大段尺寸等。
在TCP通訊中,如果一方收到另一方發來的段,讀出其中的目的埠號,發現本機並沒有任何程序使用這個埠,就會應答一個包含RST位的段給另一方。
資料傳輸的過程:
1、客戶端發出段4,包含從序號1001開始的20個位元組資料。
2、伺服器發出段5,確認序號為1021,對序號為1001-1020的資料表示確認收到,同時請求傳送序號1021開始的資料,伺服器在應答的同時也向客戶端傳送從序號8001開始的10個位元組資料,這稱為piggyback。
3、客戶端發出段6,對伺服器發來的序號為8001-8010的資料表示確認收到,請求傳送序號8011開始的資料。
在資料傳輸過程中, ACK和確認序號是非常重要的,應用程式交給TCP協議傳送的資料會暫存在TCP層的傳送緩衝區中,發出資料包給對方之後,只有收到對方應答的ACK段才知道該資料包確實發到了對方,可以從傳送緩衝區中釋放掉了,如果因為網路故障丟失了資料包或者丟失了對方發回的ACK段,經過等待超時後TCP協議自動將傳送緩衝區中的資料包重發。
關閉連線的過程:
1. 客戶端發出段7, FIN位表示關閉連線的請求。
2. 伺服器發出段8,應答客戶端的關閉連線請求。
3. 伺服器發出段9,其中也包含FIN位,向客戶端傳送關閉連線請求。4. 客戶端發出段10,應答伺服器的關閉連線請求。
建立連線的過程是三方握手,而關閉連線通常需要4個段,伺服器的應答和關閉連線請求通常不合並在一個段中,因為有連線半關閉的情況,這種情況下客戶端關閉連線之後就不能再發送資料給伺服器了,但是伺服器還可以傳送資料給客戶端,直到伺服器也關閉連線為止。
3、流量控制
如果傳送端傳送的速度較快,接收端接收到資料後處理的速度較慢,而接收緩衝區的大小是固定的,就會丟失資料。TCP協議通過'''滑動視窗(SlidingWindow) '''機制解決這一問題。
1. 傳送端發起連線,宣告最大段尺寸是1460,初始序號是0,視窗大小是4K,表示“我的接收緩衝區還有4K位元組空閒,你發的資料不要超過4K”。接收端應答連線請求,宣告最大段尺寸是1024,初始序號是8000,視窗大小是6K。傳送端應答,三方握手結束。
2. 傳送端發出段4-9,每個段帶1K的資料,傳送端根據視窗大小知道接收端的緩衝區滿了,因此停止傳送資料。
3. 接收端的應用程式提走2K資料,接收緩衝區又有了2K空閒,接收端發出段10,在應答已收到6K資料的同時宣告視窗大小為2K。
4. 接收端的應用程式又提走2K資料,接收緩衝區有4K空閒,接收端發出段11,重新宣告視窗大小為4K。
5. 傳送端發出段12-13,每個段帶2K資料,段13同時還包含FIN位。
6. 接收端應答接收到的2K資料( 6145-8192),再加上FIN位佔一個序號8193,因此應答序號是8194,連線處於半關閉狀態,接收端同時宣告視窗大小為2K。
7. 接收端的應用程式提走2K資料,接收端重新宣告視窗大小為4K。
8. 接收端的應用程式提走剩下的2K資料,接收緩衝區全空,接收端重新宣告視窗大小為6K。
9. 接收端的應用程式在提走全部資料後,決定關閉連線,發出段17包含FIN位,傳送端應答,連線完全關閉。
上圖在接收端用小方塊表示1K資料,實心的小方塊表示已接收到的資料,虛線框表示接收緩衝區,因此套在虛線框中的空心小方塊表示視窗大小,從圖中可以看出,隨著應用程式提走資料,虛線框是向右滑動的,因此稱為滑動視窗。
傳送端是一K一K地傳送資料,而接收端的應用程式可以兩K兩K地提走資料,當然也有可能一次提走3K或6K資料,或者一次只提走幾個位元組的資料,也就是說,應用程式所看到的資料是一個整體,或說是一個流( stream),在底層通訊中這些資料可能被拆成很多資料包來發送,但是一個數據包有多少位元組對應用程式是不可見的,因此TCP協議是面向流的協議。而UDP是面向訊息的協議,每個UDP段都是一條訊息,應用程式必須以訊息為單位提取資料,不能一次提取任意位元組的資料,這一點和TCP是很不同的。