1. 程式人生 > >Linux C高階程式設計——網路程式設計之TCP(3)

Linux C高階程式設計——網路程式設計之TCP(3)

Linux網路程式設計(三)——TCP

宗旨:技術的學習是有限的,分享的精神是無限的。

1TCP段格式

        和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位置132位序號是8000,該段不攜帶有效載荷(資料位元組數為0),ACK位置132位確認序號是1001,帶有一個mss選項值為1024

建立連線的過程:

1. 客戶端發出段1, SYN位表示連線請求。序號是1000,這個序號在網路通訊中用作臨時的地

址,每發一個數據位元組,這個序號要加1,這樣在接收端可以根據序號排出資料包的正確順序,也可以發現丟包的情況,另外,規定SYN位和FIN位也要佔一個序號,這次雖然沒發數據,但是由於發了SYN位,因此下次再發送應該用序號1001。 mss表示最大段尺寸,如果一個段太大,封裝成幀後超過了鏈路層的最大幀長度,就必須在IP層分片,為了避免這種情況,客戶端宣告自己的最大段尺寸,建議伺服器端發來的段不要超過這個長度。

2. 伺服器發出段2,也帶有SYN位,同時置ACK位表示確認,確認序號是1001,表示我接收到序號1000及其以前所有的段,請你下次傳送序號為1001的段,也就是應答了客戶端的連線請求,同時也給客戶端發出一個連線請求,同時宣告最大尺寸為

1024

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資料,實心的小方塊表示已接收到的資料,虛線框表示接收緩衝區,因此套在虛線框中的空心小方塊表示視窗大小,從圖中可以看出,隨著應用程式提走資料,虛線框是向右滑動的,因此稱為滑動視窗。

        傳送端是一KK地傳送資料,而接收端的應用程式可以兩KK地提走資料,當然也有可能一次提走3K6K資料,或者一次只提走幾個位元組的資料,也就是說,應用程式所看到的資料是一個整體,或說是一個流( stream),在底層通訊中這些資料可能被拆成很多資料包來發送,但是一個數據包有多少位元組對應用程式是不可見的,因此TCP協議是面向流的協議。而UDP是面向訊息的協議,每個UDP段都是一條訊息,應用程式必須以訊息為單位提取資料,不能一次提取任意位元組的資料,這一點和TCP是很不同的。