Internet 傳輸層協議本章介紹了 Internet 傳輸層的兩個重要協議 TCP 和 UDP ,包括這兩種協議的報文格式和工作原理。特別地,本章詳細介紹了 TCP 的連接建立與關閉,以及連接建立與關閉過程的狀態(tài)轉換。 3.1 引言Internet 在傳輸層有兩種主要的協議:一種是面向連接的協議 TCP ,一種是無連接的協議 UDP 。由于 UDP 基本上是在 IP 的基礎上增加一個短的報頭而得到的,比較簡單,因此本章將先介紹 UDP ,然后再重點介紹 TCP 。 在TCP/IP 協議簇中, IP 提供在主機之間傳送數據報的能力,每個數據報根據其目的主機的 IP 地址進行在 Internet 中的路由選擇。傳輸層協議為應用層提供的是進程之間的通信服務。為了在給定的主機上能識別多個目的地址,同時允許多個應用程序在同一臺主機上工作并能獨立地進行數據報的發(fā)送和接收, TCP/UDP 提供了應用程序之間傳送數據報的基本機制,它們提供的協議端口能夠區(qū)分一臺機器上運行的多個程序。 也就是說, TCP/UDP 使用 IP 地址標識網上主機,使用端口號來標識應用進程,即 TCP/UDP 用主機 IP 地址和為應用進程分配的端口號來標識應用進程。端口號是 16 位的無符號整數, TCP 的端口號和 UDP 的端口號是兩個獨立的序列。盡管相互獨立,如果 TCP 和 UDP 同時提供某種知名服務,兩個協議通常選擇相同的端口號。這純粹是為了使用方便,而不是協議本身的要求。利用端口號,一臺主機上多個進程可以同時使用 TCP/UDP 提供的傳輸服務,并且這種通信是端到端的,它的數據由 IP 傳遞,但與 IP 數據報的傳遞路徑無關。網絡通信中用一個三元組可以在全局唯一標志一個應用進程: (協議,本地地址,本地端口號) 這樣一個三元組,叫做一個半相關( half-association ),它指定連接的每半部分。一個完整的網間進程通信需要由兩個進程組成,并且只能使用同一種高層協議。也就是說,不可能通信的一端用 TCP 協議,而另一端用 UDP 協議。因此一個完整的網間通信需要一個五元組來標識: (協議,本地地址,本地端口號,遠地地址,遠地端口號) 這樣一個五元組,叫做一個相關( association ),即兩個協議相同的半相關才能組合成一個合適的相關,或完全指定組成一連接。 端口號的分配是一個重要問題。有兩種基本分配方式:第一種叫全局分配,這是一種集中控制方式,由一個公認的中央機構根據用戶需要進行統(tǒng)一分配,并將結果公布于眾。第二種是本地分配,又稱動態(tài)連接,即進程需要訪問傳輸層服務時,向本地操作系統(tǒng)提出申請,操作系統(tǒng)返回一個本地唯一的端口號,進程再通過合適的系統(tǒng)調用將自己與該端口號聯系起來(綁扎)。 TCP/UDP 端口號的分配中綜合了上述兩種方式。 TCP/UDP 將端口號分為兩部分,少量的作為保留端口,以全局方式分配給服務進程。因此,每一個標準服務器都擁有一個全局公認的端口(即周知口, well-known port ),即使在不同機器上,其端口號也相同。剩余的為自由端口,以本地方式進行分配。表 3-1 列出了常用的 TCP/UDP 周知端口號。 表 3-1 常用周知端口號列表
TCP 是一種有連接的傳輸服務,它提供可靠的傳輸,是大部分 Internet 應用的基礎。 UDP 提供的是一種無連接服務,每個數據包獨立傳輸,在傳統(tǒng)的應用中因為不能像 TCP 那樣保證數據的可靠傳輸而應用較少。但是對于新的實時視頻、音頻數據的傳輸來說,因為不能容忍 TCP 重傳帶來的時延,常常建立在 UDP 之上。 UDP 為互聯網上實時視頻、音頻服務提供了極好的實驗環(huán)境。 3.2 用戶數據報協議 UDPUDP(User Datagram Protocol) 是一個簡單的面向數據報的傳輸層協議,進程的每個輸出操作都正好產生一個 UDP 數據報,并組裝成一份待發(fā)送的 IP 數據報。 UDP 不提供可靠性,它把應用程序傳給 IP 層的數據發(fā)送出去,但是并不保證它們能到達目的地。應用程序必須關心 IP 數據報的長度。如果它超過網絡的 MTU ,那么就要對 IP 數據報進行分片。 RFC 768 [Postel 1980] 是 UDP 的正式規(guī)范。 3.2.1 UDP 報文格式 每個 UDP 報文成為一個用戶數據報,分 UDP 報頭和 UDP 數據區(qū)兩部分。報頭由四個 16 位長的字段組成,分別說明該報文的源端口、目的端口、報文長度以及校驗和。 UDP 報文格式如圖 3-1 所示。
圖 3-1 UDP 報文格式
UDP 源端口字段和目的端口字段包含了 16 位的 UDP 協議端口號,表示發(fā)送進程和接收進程。 UDP 長度字段指的是 UDP 報頭和 UDP 數據的字節(jié)長度,該字段的最小值為 8 字節(jié)(發(fā)送一份 0 字節(jié)的 UDP 數據報是可以的)。 UDP 檢驗和覆蓋 UDP 報頭和 UDP 數據。 UDP 和 TCP 在報頭中都有覆蓋它們報頭和數據的檢驗和。 UDP 的檢驗和是可選的,如果該字段值為 0 表明不進行校驗。一般來說,使用校驗和字段是必要的。 3.2.2 UDP 的封裝與協議的分層 在第 1 章介紹的 TCP/IP 協議層次結構模型中(參見圖 1-3 ), UDP 位于 IP 層之上。應用程序訪問 UDP 層,然后使用 IP 層傳送數據報,如圖 3-2 所示。 將 UDP 層放在 IP 層之上,表示一個 UDP 報文在 Internet 中傳輸時要封裝到 IP 數據報中。最后,網絡接口層將數據包封裝到一個幀中再進行物理傳輸通道上的傳輸。封裝過程如圖 3-3 所示。
由圖可知, IP 層的報頭指明了源主機和目的主機的地址,而 UDP 層的報頭指明了主機上的源端口和目的端口。 IP 層和 UDP 層之間的職責是清楚而明確的: IP 層指負責在 Internet 上的一對主機之間進行數據傳輸,而 UDP 層只負責對一臺主機上的復用的多個源端口或目的端口進行區(qū)分。 3.2.3 UDP 的復用、分解與端口
從概念上講,所有的 UDP 軟件與應用程序之間的復用和分解都要通過端口機制來實現。實際上每個應用程序在發(fā)送數據報之前必須與操作系統(tǒng)進行協商以獲得協議端口和相應的端口號。凡是利用指定的端口發(fā)送數據報的應用程序都要把端口號放入 UDP 報文中的源端口字段中。 UDP 的分解操作如圖 3-4 所示, UDP 從 IP 層接收了數據報之后,根據 UDP 的目的端口號進行分解操作。 3.3 傳輸控制協議 TCPTCP(Transfer Control Protocol) 是專門設計用于在不可靠的 Internet 上提供可靠的、端到端的字節(jié)流通信的協議。 Internet 不同于一個單獨的網絡,不同部分可能具有不同的拓撲結構、帶寬、延遲、分組大小以及其它特性。 TCP 被設計成能動態(tài)滿足 Internet 的要求,并且足以健壯地面對多種出錯。 RFC 793 [Postel 1981] 是 TCP 的正式規(guī)范。 3.3.1 可靠的數據流傳輸 UDP 提供的服務是不可靠的數據傳送服務,當傳送過程中出現差錯、網絡軟件發(fā)生故障或網絡負載太重時,分組可能會丟失,數據可能被破壞。這就需要應用程序負責進行差錯檢測和恢復工作,對傳輸數據量很大的應用來說,采用這種不可靠的數據傳輸是不合適的。因此需要有一種可靠的數據流傳輸方法,這就是 TCP 。 TCP 提供的可靠傳輸服務有如下五個特征:面向數據流:當兩個應用程序傳輸大量數據時,將這些數據當作一個可劃分為字
3.3.2 滑動窗口概念 從圖 3-5 可以看出,雖然網絡具有同時進行雙向通信的能力,但由于在接到前一個分組的確認信息之前必須推遲下一個分組的發(fā)送,簡單的肯定確認協議浪費了大量寶貴的網絡帶寬。為此, TCP 使用滑動窗口的機制來提高網絡吞吐量,同時解決端到端的流量控制。 滑動窗口技術是簡單的帶重傳的肯定確認機制的一個更復雜的變形,它允許發(fā)送方在等待一個確認信息之前可以發(fā)送多個分組。如圖 3-7 所示,發(fā)送方要發(fā)送一個分組序列,滑動窗口協議在分組序列中放置一個固定長度的窗口,然后將窗口內的所有分組都發(fā)送出去;當發(fā)送方收到對窗口內第一個分組的確認信息時,它可以向后滑動并發(fā)送下一個分組;隨著確認的不斷到達,窗口也在不斷的向后滑動。 圖 3-7 (a) 窗口內包括 8 個分組的滑動窗口協議 (b) 收到對 1 號分組的確認信息后,窗口滑動,使得 9 號分組也能被發(fā)送
滑動窗口協議的效率與窗口大小和網絡接收分組的速度有關。圖 3-8 表示了一個窗口大小為 3 的的滑動窗口協議軟件的動作示意圖。發(fā)送方在收到確認之前就發(fā)出了三個分組,在收到第一個分組的確認 ACK1 后,又發(fā)送了第四個分組。比較圖 3-5 和圖 3-8 ,就可以看出使用滑動窗口后網絡吞吐量的提高。實際上,當窗口大小等于 1 時,滑動窗口協議就等同于簡單的肯定確認協議。通過增加窗口大小,可以完全消除網絡的空閑狀態(tài)。在穩(wěn)定的情況下,發(fā)送方能以網絡傳輸分組的最快能力來發(fā)送分組。 圖 3-8 使用窗口大小為 3 的滑動窗口協議傳輸分組示例 3.3.3 TCP 報文格式 兩臺計算機上的 TCP 軟件之間傳輸的數據單元稱為報文段。 TCP 通過報文段的交互來建立連接、傳輸數據、發(fā)出確認、通告窗口大小以及關閉連接。 TCP 報文分為兩部分,前面是報頭,后面是數據。報頭的前 20 個字節(jié)格式是固定的,后面是可能的選項,數據長度最大為 65535 – 20 – 20 = 65495 字節(jié),其中第一個 20 指 IP 頭,第二個 20 指 TCP 頭。不帶任何數據的報文也是合法的,一般用于確認和控制報文。圖 3-9 給出了 TCP 報文的布局格式。每個字段的意義簡介如下:
圖 3-9 TCP 報頭格式
3.4 TCP 連接建立與關閉TCP 是一個面向連接的協議,無論哪一方向另一方發(fā)送數據之前,都必須先在雙方之間建立一條連接。本節(jié)將詳細討論一個TCP 連接是如何建立的以及通信結束后是如何終止的。 3.4.1 建立一個 TCP 連接 TCP使用三次握手 ( three-way handshake ) 協議來建立連接,圖 3-10 描述了三次握手的報文序列。這三次握手為:
發(fā)送第一個 SYN 的一端將執(zhí)行主動打開( active open ),接收這個 SYN 并發(fā)回下一個 SYN 的另一端執(zhí)行被動打開( passive open )。另外, TCP 的握手協議被精心設計為可以處理同時打開( simultaneous open ),對于同時打開它僅建立一條連接而不是兩條連接。因此,連接可以由任一方或雙方發(fā)起,一旦連接建立,數據就可以雙向對等地流動,而沒有所謂的主從關系。 三次握手協議是連接兩端正確同步的充要條件。因為 TCP 建立在不可靠的分組傳輸服務之上,報文可能丟失、延遲、重復和亂序,因此協議必須使用超時和重傳機制。如果重傳的連接請求和原先的連接請求在連接正在建立時到達,或者當一個連接已經建立、使用和結束之后,某個延遲的連接請求才到達,就會出現問題。采用三次握手協議(加上這樣的規(guī)則:在連接建立之后 TCP 就不再理睬又一次的連接請求)就可以解決這些問題。 三次握手協議可以完成兩個重要功能:它確保連接雙方做好傳輸準備,并使雙方統(tǒng)一了初始順序號。初始順序號是在握手期間傳輸順序號并獲得確認:當一端為建立連接而發(fā)送它的 SYN 時,它為連接選擇一個初始順序號;每個報文段都包括了順序號字段和確認號字段,這使得兩臺機器僅僅使用三個握手報文就能協商好各自的數據流的順序號。一般來說, ISN 隨時間而變化,因此每個連接都將具有不同的 ISN 。 3.4.2 關閉一個 TCP 連接 TCP 連接建立起來后,就可以在兩個方向傳送數據流。當 TCP 的應用進程再沒有數據需要發(fā)送時,就發(fā)關閉命令。 TCP 通過發(fā)送控制位 FIN=1 的數據片來關閉本方數據流,但還可以繼續(xù)接收數據,直到對方關閉那個方向的數據流,連接就關閉。 TCP 協議使用修改的三次握手協議來關閉連接, 如圖 3-11 所示,即終止一個連接要經過 4 次握手。這是因為 TCP 的半關閉( half-close )造成的。由于一個 TCP 連接是全雙工(即數據在兩個方向上能同時傳遞),因此每個方向必須單獨地進行關閉。關閉的原則就是當一方完成它的數據發(fā)送任務后就能發(fā)送一個 FIN 來終止這個方向連接。當一端收到一個 FIN ,它必須通知應用層另一端已經終止了那個方向的數據傳送。發(fā)送 FIN 通常是應用層進行關閉的結果。 從一方的 TCP 來說,連接的關閉有三種情況: • 本方啟動關閉 收到本方應用進程的關閉命令后, TCP 在發(fā)送完尚未處理的報文段后,發(fā) FIN = 1 的報文段給對方,且 TCP 不再受理本方應用進程的數據發(fā)送。在 FIN 以前發(fā)送的數據字節(jié),包括 FIN ,都需要對方確認,否則要重傳。注意 FIN 也占一個順序號。一旦收到對方對 FIN 的確認以及對方的 FIN 報文段,本方 TCP 就對該 FIN 進行確認,在等待一段時間,然后關閉連接。等待是為了防止本方的確認報文丟失,避免對方的重傳報文干擾新的連接。 • 對方啟動關閉 當 TCP 收到對方發(fā)來的 FIN 報文時,發(fā) ACK 確認此 FIN 報文,并通知應用進程連接正在關閉。應用進程將以關閉命令響 應。 TCP 在發(fā)送完尚未處理的報文段后,發(fā)一個 FIN 報文給對方 TCP ,然后等待對方對 FIN 的確認,收到確認后關閉連接。若對方的確認未及時到達,在等待一段時間后也關閉連接。 • 雙方同時啟動關閉 連接雙方的應用進程同時發(fā)關閉命令,則雙方 TCP 在發(fā)送完尚未處理的報文段后,發(fā)送 FIN 報文。各方 TCP 在 FIN 前所發(fā)報文都得到確認后,發(fā) ACK 確認它收到的 FIN 。各方在收到對方對 FIN 的確認后,同樣等待一段時間再關閉連接。這稱之為同時關閉( simultaneous close )。 3.4.3 TCP 狀態(tài)機 TCP 協議的操作可以使用一個具有 11 種狀態(tài)的有限狀態(tài)機( Finite State Machine )來表示,圖 3-12 描述了 TCP 的有限狀態(tài)機,圖中的圓角矩形表示狀態(tài),箭頭表示狀態(tài)之間的轉換,各狀態(tài)的描述如表 3-2 所示。圖中用粗線表示客戶端主動和被動的服務器端建立連接的正常過程:客戶端的狀態(tài)變遷用粗實線,服務器端的狀態(tài)變遷用粗虛線。細線用于不常見的序列,如復位、同時打開、同時關閉等。圖中的每條狀態(tài)變換線上均標有“事件/動作”:事件是指用戶執(zhí)行了系統(tǒng)調用( CONNECT 、 LISTEN 、 SEND 或 CLOSE )、收到一個報文段( SYN 、 FIN 、 ACK 或 RST )、或者是出現了超過兩倍最大的分組生命期的情況;動作是指發(fā)送一個報文段( SYN 、 FIN 或 ACK )或什么也沒有(用“-”表示)。 圖 3-12 TCP 有限狀態(tài)機。粗實線表示客戶的正常路徑; 每個連接均開始于 CLOSED 狀態(tài)。當一方執(zhí)行了被動的連接原語( LISTEN )或主動的連接原語( CONNECT )時,它便會脫離 CLOSED 狀態(tài)。如果此時另一方執(zhí)行了相對應的連接原語,連接便建立了,并且狀態(tài)變?yōu)?ESTABLISHED 。任何一方均可以首先請求釋放連接,當連接被釋放后,狀態(tài)又回到了 CLOSED 。
表 3-2 TCP 狀態(tài)表
1. 正常狀態(tài)轉換 我們用圖 3-13 來顯示在正常的 TCP 連接的建立與終止過程中,客戶與服務器所經歷的不同狀態(tài)。讀者可以對照圖 3-12 來閱讀,使用圖 3-12 的狀態(tài)圖來跟蹤圖 3-13 的狀態(tài)變化過程,以便明白每個狀態(tài)的變化:
在此狀態(tài)下,雙方可以自由傳輸數據。當一個應用程序完成數據傳輸任務后,它需要關閉 TCP 連接。假設仍由客戶端發(fā)起主動關閉連接。
2. 同時打開: 盡管發(fā)生的可能性極小,兩個應用程序同時彼此執(zhí)行主動打開的情況還是可能的。每一方必須發(fā)送一個 SYN ,且這些 SYN 必須傳遞給對方。這需要每一方使用一個對方周知的端口作為本地端口。例如,主機 A 中的一個應用程序使用本地端口 7777 ,并與主機 B 的端口 8888 執(zhí)行主動打開。主機 B 中的應用程序則使用本地端口 8888 ,并與主機 A 的端口 7777 執(zhí)行主動打開。 TCP 是特意設計為了可以處理同時打開,對于同時打開它僅建立一條連接而不是兩條連接(其他的協議族,最突出的是 OSI 傳輸層,在這種情況下將建立兩條連接而不是一條連接)。 當出現同時打開的情況時,狀態(tài)變遷與圖 3-13 所示的不同。兩端幾乎在同時發(fā)送 SYN ,并進入 SYN_SENT 狀態(tài)。當每一端收到 SYN 時,狀態(tài)變?yōu)?SYN_RCVD ,同時它們都再發(fā) SYN 并對收到的 SYN 進行確認。當雙方都收到 SYN 及相應的 ACK 時,狀態(tài)都變遷為 ESTABLISHED 。圖 3-14 顯示了這些狀態(tài)變遷過程。 圖 3-14 同時打開期間報文段的交換
一個同時打開的連接需要交換 4 個報文段,比正常的三次握手多一個。此外,要注意的是我們沒有將任何一端稱為客戶或服務器,因為每一端既是客戶又是服務器。
3. 同時關閉: 正常情況下都是由一方(通常但不總是客戶方)發(fā)送第一個 FIN 執(zhí)行主動關閉,但雙方都執(zhí)行主動關閉也是可能的, TCP 協議也允許這樣的同時關閉。 在圖 3-12 中,當兩端應用層同時發(fā)出關閉命令時,兩端均從 ESTABLISHED 變?yōu)?FIN_WAIT_1 。這將導致雙方各發(fā)送一個 FIN ,兩個 FIN 經過網絡傳送后分別到達另一端。收到 FIN 后,狀態(tài)由 FIN_WAIT_1 變遷到 CLOSING ,并發(fā)送最后的 ACK 。當收到最后的 ACK 時,狀態(tài)變化為 TIME_WAIT 。圖 3-15 總結了這些狀態(tài)的變化,從圖中可以看出同時關閉與正常關閉使用的報文段交換數目相同。 圖 3-15 同時關閉期間的報文段交換
4. 其它情況:
另外, TIME_WAIT 狀態(tài)的等待超時需要再詳細解釋一下,因為它直接影響到網絡應用程序的表現。 每個具體 TCP 實現必須選擇一個報文段最大生存時間 MSL ( Maximum Segment Lifetime ),它是任何報文段被丟棄前在網絡內的最長時間。我們知道這個時間是有限的,因為 TCP 報文段以 IP 數據報在網絡內傳輸,而 IP 數據報有限制其生存時間的 TTL 字段。 RFC 793 [Postel 1981c ] 指出 MSL 為 2 分鐘。然而,實現中的常用值是 30 秒、 1 分鐘、或 2 分鐘。 對一個具體實現所給定的 MSL 值,處理的原則是:當 TCP 執(zhí)行一個主動關閉,并發(fā)回最后一個 ACK ,該連接必須在 TIME_WAIT 狀態(tài)停留的時間為 2 倍的 MSL ,因此 TIME_WAIT 狀態(tài)也稱為 2MSL 等待狀態(tài)。在這段時間內,如果最后的 ACK 丟失,對方會超時并重發(fā)最后的 FIN ,這樣本地 TCP 可以再次發(fā)送 ACK 報文段(這也是它唯一可以發(fā)送的報文,并重置 2MSL 定時器)。 這種 2MSL 等待的另一個結果是這個 TCP 連接在 2MSL 等待期間,定義這個連接的套接字( socket ,客戶的 IP 地址和端口號,服務器的 IP 地址和端口號)不能再被使用。這個連接只能在 2MSL 結束后才能再被使用。在連接處于 2MSL 等待時,任何遲到的報文段將被丟棄。 我們假設圖 3-12 中是客戶執(zhí)行主動關閉并進入 TIME_WAIT ,這是正常的情況,因為服務器通常執(zhí)行被動關閉,不會進入 TIME_WAIT 狀態(tài)。這暗示如果我們終止一個客戶程序,并立即重新啟動這個客戶程序,則這個新客戶程序將不能重用相同的本地端口。這不會帶來什么問題,因為客戶使用本地端口,而并不關心這個端口號是什么。然而,對于服務器,情況就有所不同,因為服務器使用周知端口。如果我們終止一個已經建立連接的服務器程序,并試圖立即重新啟動這個服務器程序,服務器程序將不能把它的這個周知端口賦值給它的端點,因為那個端口是處于 2MSL 連接的一部分。在重新啟動服務器程序前,它需要在 1~4 分鐘。這就是很多網絡服務器程序被殺死后不能夠馬上重新啟動的原因(錯誤提示為“ Address already in use ”)。 3.5 小結和 IP 一樣, TCP 也是 TCP/IP 協議簇中最為重要的協議。本章詳細地介紹了 TCP 的工作原理、數據報格式,特別是 TCP 連接建立與關閉的狀態(tài)轉換。由于 TCP 是離應用層最近的底層協議,因此 TCP 的很多狀態(tài)在上層應用中是可見的,在后面的章節(jié)中,很多地方都涉及到 TCP 的各種狀態(tài)。因此,掌握本章介紹的原理和概念,對于學好后面的章節(jié)極為有利,特別是掌握了 TCP 的狀態(tài)轉換規(guī)律,對于網絡的配置與診斷、以及網絡應用的調試大有裨益。 習 題
|
|