1. 程式人生 > 實用技巧 >UNP筆記-傳輸層概述

UNP筆記-傳輸層概述

使用者資料報協議(UDP)

UDP是一個簡單的傳輸層協議,當應用層向UDP套接字寫入一個訊息,此訊息會被封裝到一個UDP資料報,進而又被封裝到一個IP資料報傳送到目的地。

UDP不保證資料報會到達目的地,不保證多個數據報的先後順序,不保證每個資料報只到達一次。

UDP提供無連線的服務,因為UDP客戶端與伺服器之間不存在任何長連線關係。

傳輸控制協議(TCP)

完全不同於UDP,TCP提供了可靠性傳輸。TCP需要客戶端與伺服器之間建立連線,然後通過這個連線進行交換資料,最後終止連線。

TCP有動態估算客戶端和伺服器之間的往返時間RTT演算法。

TCP通過給其中每個位元組關聯一個序號對傳送的資料進行排序。接受端TCP將根據序號重新排序,再傳遞給接受應用。

TCP提供流量控制。

TCP是全雙工的。建立的連線在任意時刻兩個端點都可以傳送且接受資料。UDP可以是全雙工的。

TCP 連線的建立和終止

TCP 連線建立 (三次握手)

TCP建立連線需要三個分節。

  1. 伺服器被動開啟。通過呼叫socket、bind、listen三個函式來準備接受客戶端連線。

  2. 客戶端通過呼叫connect發起主動開啟。客戶端TCP傳送一個SYN,告訴伺服器客戶端在待建立的連線中傳送的資料的初始序列號。

  3. 伺服器確認客戶端的SYN,向客戶端傳送ACK,同時伺服器也傳送一個SYN是伺服器在改連線中傳送的資料的初始序列號。

  4. 客戶端必須確認並向伺服器傳送SYN。

圖中客戶端給的初始序列號為J

,伺服器給的初始序列號為K。ACK中的確認號就是接受這個ACK一端所期待的下一個序列號。

TCP連線終止 (四次揮手)

TCP終止連線需要四個分節。

  1. 假如客戶端首先呼叫close,則客戶端為主動關閉。該TCP向伺服器傳送一個FIN,表示資料傳送完畢。
  2. 伺服器確認收到FIN執行被動關閉。並向客戶端傳送ACK。
  3. 伺服器端傳遞到應用程序,應用程序呼叫close關閉套接字。於是伺服器的TCP也傳送一個FIN給客戶端。
  4. 客戶端確認收到伺服器的FIN,並向伺服器傳送ACK。

與SYN類似,一個FIN也佔據一個位元組序列號空間。每個FIN的ACK確認號就是這個FIN的序列號加1。

TCP 狀態轉換圖

TCP涉及的連線建立和連線終止的操作可以用狀態轉換圖說明。

圖中,實線表示客戶端的正常狀態轉換,虛線表示伺服器的正常狀態轉換。

TCP 分組交換圖

下圖展示了一個完整的TCP連線發生的實際分組交換情況,包括連線建立、資料傳送、連線終止。

TIME_WAIT 狀態

從上面的圖可以看到,執行了主動關閉的客戶端會產生TIME_WAIT狀態,該客戶端會停留在這個狀態持續時間是最長分節生命週期2倍,也就是2MSL

MSL是任何IP資料報能夠在英特網中存活的最長時間。任何TCP實現都有一個MSL值,RFC中建議是2分鐘,但是有些實現上改為了30秒。也就是TIME_WAIT狀態持續時間為1分鐘~4分鐘。

存在TIME-WAIT狀態有兩個理由

  1. 實現終止TCP全雙工連線的可靠性

  2. 允許老的重複分節在網路中消逝

第一個理由:
假設最終的響應ACK丟失,伺服器將重發最終的FIN,因此客戶必須維護狀態資訊以允許它重發最終的ACK。如果不維護狀態資訊,它將響應以RST(另外一個型別的TCP分節),而伺服器則把該分節解釋成一個錯誤。如果TCP打算執行所有必要的工作以徹底終止某個連線上兩個方向的資料流(即全雙工關閉),那麼它必須正確處理連線終止序列四個分節中任何一個分節的丟失情況。本例子也說明執行主動關閉的一端為什麼進入TIME_WAIT狀態,因為它可能不得不重發最終的ACK。

第二個理由:
我們假設206. 62. 226. 33埠1500和198. 69.10.2埠21之間有一個TCP連線。我們關閉這個連線後,在以後某個時候又重新建立起相同的IP地址和埠之間的TCP連線。後一個連線稱為前一個連線的化身(incar-nation).因為它們的IP地址和埠號都相同。TCP必須防止來自某個連線的老重複分組在連線終止後再現,從而被誤解成屬於同一連線的化身。要實現這種功能,TCP不能給處於TIME-WAIT狀態的連線啟動新的化身。既然TIME-WAIT狀態的持續時間是2MLS,這就足夠讓某個方向上的分組最多存活MSL秒即被丟棄,另一個方向上的應答最多存活MSL秒也被丟棄。通過實施這個規則,我們就能保證當成功建立一個TCP連線時,來自該連線先前化身的老重複分組都已在網路中消逝。

CLOSE_WAIT 狀態

CLOSE_WAIT是被動關閉連線是形成的。

根據TCP狀態機,伺服器端收到客戶端傳送的FIN,則按照TCP實現傳送ACK,因此進入CLOSE_WAIT狀態。但如果伺服器端不執行close(),就不能由CLOSE_WAIT遷移到LAST_ACK,則系統中會存在很多CLOSE_WAIT狀態的連線。

可能是系統忙於處理讀、寫操作而未將已收到FIN的連線進行close。此時,recv/read已收到FIN的連線socket會返回0。

埠號

應用層有很多程序都需要使用TCP或UDP等協議。這些協議都是使用了16位整數的埠號來區分這些程序。客戶端想要和伺服器通訊,必須知道與之通訊的伺服器的IP地址和埠號。客戶端通常使用短期存活的臨時埠。

TCP定義了一些總所周知的埠號0~1023,比如無論TCP還是UDP的80埠都是web伺服器,儘管大多實現是TCP。這些埠由IANA分配。

已登記的埠號為1024~49151,不受IANA控制,但由IANA登記並提供它們的使用情況。

動態/私有埠號為49152~65535,IANA不管之這些埠。

需要注意的是:

  1. UNIX系統會保留埠,小於1024的埠只能賦予特權使用者程序的套接字。

TCP 套接字對

一個TCP套接字對是一個定義該連結的兩個端點的四元組:本地IP地址,本地TCP埠,外地IP地址,外地TCP埠。

套接字對唯一標識一個網路上每個TCP的連線。

標識每個端點的兩個值(IP&Port)通常稱為一個套接字。

TCP 套接字緩衝區

沒一個TCP套接字都一個傳送緩衝區,可以使用SO_SNDBUF來更改該緩衝區的大小。

當應用層程序呼叫write時,核心從改應用程序的緩衝區中複製所有資料到套接字的傳送緩衝區。如果應用層程序緩衝區大於套接字傳送緩衝區, 該程序將投入隨眠(假設該套接字是阻塞的,預設)。核心將不從write系統呼叫返回,知道應用層程序緩衝區所有資料都被複制到套接字傳送緩衝區。

所以呼叫wirte成功返回並不代表對端的TCP已經收到資料。

UDP 套接字緩衝區

任何UDP套接字都有傳送緩衝區大小,不過它僅僅是可以寫到該套接字的UDP資料的大小上限。

如果一個應用層程序寫一個大於套接字傳送緩衝區大小的資料報,核心將返回該程序一個EMSGSIZE錯誤,既然UDP是不可靠的,它不必儲存應用層程序資料的一個副本,因此無需一個真正的傳送緩衝區。