| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
我們在前面討論 OSI 和 TCP/IP 分層協定概念的時候﹐已經指出﹕不管協定設計者如何定義層級﹐各層級協定大致分成兩類﹕網路群組、和使用者群組。前面介紹的 ARP 、IP、RIP 等協定﹐可以算是網路群組的範圍﹐假如您對前述概念都有一定認識﹐已經知道了一個封包如何從一個節點傳遞到另一個節點。然而﹐這僅是 TCP/IP 協定的一半而已﹐要完全了解 TCP/IP 的精髓﹐在 IP 協定的更上一個層級﹐屬於使用者群組協定之一﹕TCP 協定﹐是不可不知道的。只有當我們同時把 IP 協定和 TCP 協定理解進來﹐才能完整的描述電腦與電腦之間的資料傳送過程﹔也只有如此﹐我們才有把握進行日常的 TCP/IP 網路管理。TCP 與 IP 這對孿生兄弟﹐是每一個網路管理人員必須混熟的朋友。下面﹐我們將一起探討在 TCP/IP 協定中舉足輕重的傳送層﹐是如何影響我們日常的網路資料傳輸的。 傳送層的功能 在前面討論網際網路層的時候﹐我們知道﹕網際網路層協定只提供路由資訊的判斷﹐以確定封包的傳送路徑。但事實上 IP 協定只確保封包交換設備之間的傳輸﹐並沒有提供一套機制來確保數據的傳輸。在低層的通訊裡﹐封包可能在傳送過程中發生錯誤﹐諸如網路硬體的損壞、網路負荷過重等等﹐導致封包被丟棄或損壞。由於封包路由的多樣性和複雜性﹐以及影響路由因素眾多及其不可預測性﹐封包之抵達常是不依序的﹐或是會發生重複傳送的情形。因此﹐我們必須提供一套網路技術﹐以達成更可靠和有效的傳送。 再者﹐ IP 封包的體積是有限的﹐然而﹐網路程式之間交換的數據往往會超過這個體積限制﹔那麼﹐我們必須有另一套機制將程式送來的資料進行規劃﹐以符合 IP 封包的傳送要求。在高層的程式裡﹐除非利用非可靠和非連線型(connectionless)的資料傳送方式﹐否則,程式設計者必須對每一個一個應用程式處理偵錯和修復的動作﹐這無疑增加了程式設計和修改的難度﹐而且也做成許多重複的處理動作。因此﹐我們也有必要找出一個可靠的資料流傳送方法﹐以建立單獨且適用於所有應用程式的資料傳送協定。這樣就可以將應用程式與網路內部協定隔離﹐同時提供一致的資料流傳送界面。 傳送層的設計可以說是應上述要求而生的﹐它的主要功能有﹕
在 TCP/IP 協定組中,關於傳送層的協定就是 TCP 和 UDP 了﹐我們將在下面詳細討論。簡而言之﹐TCP 提供的是一個可靠的資料流傳送服務﹔相對而言﹐UDP 提供的是一個非可靠的非連線型(connectionless)的資料流傳送服務。 可靠性傳送服務的特性 在應用程式對 TCP 的可靠性傳送服務之主要要求有五個﹕
TCP 協定在進行傳輸的時侯,必須依靠 IP 協定傳送封包。相對於 TCP , IP 協定屬於不可靠協定﹐因為兩個協定必需同時困綁工作,因此只要其一能做到可靠傳輸就可以了。要詳細的描述 TCP 如何提供可靠性傳送是非常複雜的﹐但大部分可靠性協定都採用一定的確認機制來保證傳送之可靠性。這種技術需要接收端以確認信息(Acknowledgement) 回應發送端﹐肯定資料無誤的到達﹐同時雙方保留傳送的封包記錄﹐以作下一筆資料的確認依據。此外﹐還利用定時器的機制﹐以在傳送逾時後重新發送封包,以確保資料的完整性。我們可以從下圖中看到確認機制的簡單模式﹕ 可靠性傳送的確認機制 發送端在送出封包之後﹐會起始一個專門針對該封包的計時器﹐當下層網路延遲過久導致封包不能按預估時間獲得接收端的確認信息﹐那麼發送端會認為該封包可能在傳送過程中丟失﹐然後會重新發送該封包、並同時重設計時器﹔如果封包的確認信息在逾時前被接收到﹐則取消該封包的計時器﹐以進行下一封包的傳送。 可靠性傳送的計時器原理 計時器雖然解決了封包丟失的問題﹐但如果封包的抵達只是因為網路延遲的關係沒有在預定時間完成﹐但卻在發送端重發後抵達﹐那麼﹐接收端就有可能接收到重複的封包。為解決這個困繞﹐傳送協定會為每一個封包分配一個序號﹐並要求接收端按封包序號傳回確認信息。這樣﹐當接收端收到封包的時候﹐則可以依據序號判斷封包是否被重複傳送,同時也能正確的重組資料順序﹔而發送端也能根據確認封包的序號來判斷封包是否被正確接收。 滑動視窗(Sliding Window) 從剛才介紹的可靠性傳送知識作一個推斷:假如每一個單一封包都需要需要等待前面的封包確認之後才進行傳送的話﹐將會導致整個連線過程時間的增加﹐同時也會造成頻寬的浪費。假如在低速的網路上面﹐或設備延遲﹐甚至還會造成網路處於空閒狀態。有鑒於此﹐聰明的傳送層協定設計者們引入了一個滑動視窗的概念。 我們可以將滑動視窗理解為多重發送和多重確認的技術。它允許發送端在接收到確認信息之前同時傳送多個封包﹐因而能夠更充份的利用網路頻寬和加速資料傳送速度。滑動視窗的操作可以想像為下圖﹕ 滑動視窗的工作原理 我們利用 Sliding Window 在收發兩端各劃分出一個緩衝範圍(buffer)﹐定義了多大的資料量可被打包傳送。在連線建立起來之初﹐兩端都會將 window 的設定值還原到初始值﹐比方說﹕ 3 個封包。發送端一次過發送三個封包出去﹐如果接收端夠順利﹐也能一次處理接收下來的三個封包的話﹐就會向發送端確認全部三個封包,並告知接收端之 window 值為 3 。然後,發送端視窗則會往後移動三個封包﹐填補發送出去之封包的空缺。但如果接收端太忙﹐或是其它因素影響﹐暫時只能處理兩個封包﹐那麼﹐在視窗裡面就剩下一個封包﹐然後就會告訴發送端 window 值為 2。這個時候﹐發送端就只送出兩個封包﹐而視窗就會往後移動兩個封包﹐填補發送出去的空缺。因此,視窗的大小是不固定的,這就是為什麼我們會在視窗前面加上“滑動”字眼的原因了。(注意:這裡使用封包數目作 window size 是不正確的,僅作例子參考而已。實際上的單位應是位元組,視不同的作業系統而各有不同,一般為 4096 bytes,但也有擴展至 16384 bytes 的。而且視窗的 size 是每個確認封包都不同的,端視當前的緩衝區狀況,其機制比前述複雜許多。) 在啟動滑動視窗之後﹐封包的傳送看起來如下圖﹕ 啟動滑動視窗後的傳送 滑動視窗會記住哪些封包已經被確認﹐並且為每一個未被確認的封包保留各自的計時器。如果在逾時後還沒得到該封包的確認﹐則重發該封包。發送端在移動視窗的時候﹐它會移過所有已確認的封包。在視窗中﹐編號最低的封包﹐往往是序列中的第一個未被確認的封包。 通訊埠口(port) 大多數的作業系統都提供多工環境﹐允許多個應用程式同時執行﹐在系統術語裡面﹐我們管每一個程式的起止為一個行程。每一個行程都是動態產生的﹐發送端無法預知接收端的某一個行程的實際狀況如何。那麼﹐當一個封包抵達目的地之後﹐接收如何將封包交給正確的行程處理呢﹖ 在傳送層協定裡面﹐我們為程式產生的行程分配一個通訊埠口﹐其值為一個正正數。當一個應用程式需要建立網路連線的時候﹐傳送層協定就為該應用程式產生的行程建立一個埠口。而事實上,所謂的網路連線,就是兩個通訊埠口之間的連線。關於網路通訊模式的建立有兩種﹕
主動連線是當埠口建立之後﹐行程透過該埠口主動發出連線的要求﹔被動模式則是﹐當埠口建立之後﹐行程在該埠口等待連線的請求。在 client/server 的架構之下,連線的建立順序通常是伺服器端先建立好被動連線﹐然後等待客戶端的主動連線。 在技術上﹐行程使用哪一個埠口並不重要﹐關鍵是能讓對方知道埠口是哪一個就行。我們可以把 IP 位址看成主機的門牌號碼﹐而埠口則是服務櫃檯。在多工的環境下﹐行程會在一個門牌上面開啟多個櫃檯。您或許會問:由起始端主動發起之連線封包抵達之後﹐它究竟憑什麼來判斷究竟哪個櫃檯才是正確的行程呢?在日常的生活中﹐大不了逐個櫃檯去問... 然而在網路系統上面﹐這個似乎有點不切實際。因為﹐每一個埠口的建立和關閉都是隨機的﹐在不同的時段裡﹐所開啟的埠口數目和號碼都不盡相同。既然如此﹐等待連線那端何不先將接收行程所使用埠口號碼告知起始端呢?但問題是:既然連線要由起始端主動建立才能連上等待端﹐在沒有真正連上之前如何得知呢?不是雞生蛋、蛋生雞的問題嗎? 有見及此﹐在網際網路的實作應用中﹐人們將一些常用的服務程式所使用埠口號碼固定起來。例如﹕21 給 FTP 服務使用、23 給 TELNET 服務使用、25 給 SMTP 服務使用... 我們稱這樣的埠口為 Well-Known Port。在伺服器端﹐這些常用服務會先行建立好被動連線﹐打開所分配的埠口﹐以等待起始端的連線請求。那麼﹐起始端只要在封包填上目的端的埠口值﹐接收端就能將封包傳給正確的服務了, 這也就是透過約定俗成的分配來建立連線。但事實上,您大可架設一個地下網站,故意使用其它非 Well-Known Port 來建立被動連線埠口,這樣,只有那些事先被告知埠口值、且能修改主動連線設定的客戶端才知門而入了。 除了使用 Well-Know Port 讓起始端知道如何建立連線之外﹐傳送協定也允許行程在連線建立之後﹐另行指定新的埠口﹐告知發送端以建立新的連線。例如﹐ftp-data 連線在被動模式下的建立就是一個極佳例子(請參考其它文件以了解 ftp-data 的被動模式)。 無論如何,關於可靠傳輸的一個重要特性,我們不能忘記的一個特性就是﹕一個連線是雙向的。在每一個傳送層封包中﹐除了包含目的端的埠口值﹐同時也會附上接收端的來源埠口值。這樣﹐目的端才能將回應資料送返發送端。不過﹐在大多數情況之下﹐起始端所使用的來源埠口都是隨機產生的﹐只有在連線建立的時候才被分配﹐一旦連線結束﹐該埠口也將被釋放。 即使隨後馬上再建立相同的連線,也難以確保所使用的埠口與上一次的一致。事實上,沒有任何人、任何機器、任何程式(包括建立主動連線的程式) ,能夠"預知"下一個起始連線會被分配在哪一個埠口之上。我們充其量,只能知到一般客戶端所能建立的埠口值會是 1024 至 65535 之間。也只能知到如此而已,至於具體會是哪個埠口,就沒辦法預知了。請好好記住此一特性,日後在防火牆設定上可是非常有用哦。 Socket Pair 從前面的知識中,我們可以得知,所謂的一個網路連線,事實上就是兩台機器之間的兩個程式之間的連線。我們可以根據 IP 來區別主機、根據埠口(port)來區別程式。在 TCP/IP 連線中,這是非常重要的概念,也就是所謂的 Socket 啦。 一個 Socket 就是由一個 IP 與一個 Port 來定義的,您可將之視為程式與 TCP/IP 連線之間的界面。準確來說﹐一個連線封包必須有四個元素,也就是所謂的 Socket Pair﹕
任何一個 TCP/IP 封包都肯定帶有這對 Socket 的資訊,缺一不可。然而,來源 Socket 與目的 Socket 卻是相對而言的,若裡開了封包本身的具體連線方向,是沒辦法區分來源與目的的。因為連線是雙向的緣故,若封包從客戶端送往伺服器端,那麼:客戶端為來源、伺服器端為目的﹔若是從伺服器端送往客戶端,則剛好相反。網際網路層依靠位址資訊將封包送抵目的地、處理完畢後將封包交由傳送層處理、然後傳送層則依據埠口值、將封包交由相應的程式處理、至於程式如何處理封包信息﹐那就是應用層所要關心的問題... 這正是我們從 OSI 模型中學到的 "封裝" 概念! 從編程的角度來說﹐程式不必理會底層的封包是如何傳送的﹐只要程式能開啟一個 socket ﹐並能對之進行讀或寫的動作﹐就可以與另一方的程式溝通了。至於 socket 的建立與維護﹐則交給傳送層協定負責。 TCP 與 UDP 在 TCP/ IP 協定家族中﹐傳送層主要有兩個協定﹕TCP 與 UDP。究竟兩者有何不同呢﹖簡單而言﹐TCP 提供的是一個連線導向(Connection Oriented)的可靠傳輸﹐前面所介紹的傳送層檢測手續﹐都會在 TCP 中得到實現。相對而言﹐UDP 則是一個非連線型(Connectionless)的非可靠傳輸協定﹐它並不會運用確認機制來保證資料是否正確的被接收、不需要重傳遺失的資料、資料的接收可不必按順序進行、也不提供回傳機制來控制資料流的速度。因此﹐ UDP 信息可能會在網路傳送過程中丟失、重複、或不依順序﹐而且抵達速度也可能比接收端的處理速度還快。對於某些訊息量較大、時效性大於可靠性的傳輸來說(比方說語音 / 影像),UDP 的確是個不錯的選擇。 從 OSI 模型的封裝原理中我們得知:一個網路封包就是經過層層加封的結果。其中,拿掉 header 的部份,就是 payroll 的空間、也就是上層協定封包及資料。然而,真正交由網路傳送的 IP 封包是有一定的體積限制的( IP 封包的最大體積為 65536 bytes )。由於 UDP 不需要可靠傳輸,因此相較於 TCP 來說,一大堆必需佔據封包表頭的 over head 都可省略,從而換取更大的 payroll 空間。這樣的結果,將令到單一的 IP 封包在作 UDP 連線時所攜載的資料要比 TCP 連線多更多。這是靠犧牲可靠性而換取得來的,若連線需要在 UDP 上作可靠傳輸,那麼,其確認機制將從傳輸層退為應用層進行了、也就是程式本身要提供可靠傳輸機制。 下面﹐我們將分別以 TCP 和 UDP 的封包表頭格式做更進一步的說明﹐以了解這兩個傳送層協定的異同之處。 TCP 封包表頭格式 一個 TCP 封包的表頭包括有如下欄位﹕
下面是各欄位的名稱和定義﹕
UDP 封包表頭格式 因為 UDP 是一種非可靠、非連線型的傳輸協定,因此無須像 TCP 那樣提供額外的欄位來控制傳輸可靠性。比起 TCP 來說,UDP 的封包表頭可精簡多了:
TCP 還是 UDP ? 如前所述,TCP 與 UDP 主要的差異在於是否提供可靠性傳輸。其真正目的是為上層應用程式提供不同的傳輸選擇:
因此,不同的應用程式協定,會跟據自身的資料特性來決定其所需的傳輸服務。在整個 TCP/IP 協定家族中,其層階關係如下圖: TCP/IP 協定家族 請再次運用所學的 OSI 模型原理,好好理解 TCP/IP 協定之層級關係,自然就有更明晰的觀念了。若您懂得運用工具分析封包結構,將更有幫助。如下圖,是我在 Windows 上用 netxray 抓到的一個封包,看您是否能解讀出其中的欄位?
TCP 協定之 RFC 文件
UDP 協定之 RFC 文件
習題﹕
www.study-area.org © 2002 Netman 網中人Last Updated: October 12, 2002 |