網絡編程中最基本的概念就是面向連接(connection-oriented)和無連接(connectionless)協議。盡管本質上來說,兩者之間的區別並不難理解,但對那些剛剛開始進行網絡編程的人來說,卻是個很容易混淆的問題。這個問題與上下文有些關聯:很顯然,如果兩台計算機要進行通信,就必須以某種形式“連接”起來,那“無連接通信”又是什麼意思呢?
答案是:面向連接和無連接指的都是協議。也就是說,這些術語指的並不是物理介質本身,而是用來說明如何在物理介質上傳輸數據的。面向連接和無連接協議可以,而且通常也確實會共享同一條物理介質。
如果兩者的區別與承載數據的物理介質無關,又和什麼有關呢?它們的本質區別在於,對無連接協議來說,每個分組的處理都獨立於所有其他分組,而對面向連接的協議來說,協議實現則維護了與後繼分組有關的狀態信息。
無連接協議中的分組被稱為數據報(datagram),每個分組都是獨立尋址,並由應用程序發送的。從協議的角度來看,每個數據報都是一個獨立的實體,與在兩個相同的對等實體之間傳送的任何其他數據報都沒有關系,這就意味著協議很可能是不可靠的。也就是說,網絡會盡最大努力傳送每一個數據報,但並不保證數據報不丟失、不延遲或者不錯序傳輸。
另一方面,面向連接的協議則維護了分組之間的狀態,使用這種協議的應用程序通常都會進行長期的對話。記住這些狀態,協議就可以提供可靠的傳輸。比如,發送端可以記住哪些數據已經發送出去了但還未被確認,以及數據是什麼時候發送的。如果在某段時間間隔內沒有收到確認,發送端可以重傳數據。接收端可以記住已經收到了哪些數據,並將重復的數據丟棄。如果分組不是按序到達的,接收端可以將其保存下來,直到邏輯上先於它的分組到達為止。
典型的面向連接協議有三個階段。第一階段,在對等實體間建立連接。接下來是數據傳輸階段,在這個階段中,數據在對等實體間傳輸。最後,當對等實體完成數據傳輸時,連接被拆除。
一種標准的類比是:使用無連接協議就像寄信,而使用面向連接的協議就像打電話。
給朋友寄信時,每封信都是一個獨立尋址且自包含的實體。郵局在處理這些信件時不會考慮到兩個通信者之間的任何其他信件。郵局不會維護以往通信者的歷史記錄--也就是說,它不會維護信件之間的狀態。郵局也不保證信件不丟失、不延遲、不錯序。這種方式就對應於無連接協議發送數據報的方式。(用明信片進行類比會更合適一些,因為寫錯地址的信件會被退回發信人,而(和典型的無連接協議數據報一樣)明信片則不會。)
現在來看看不是給朋友寄信,而是打電話時會發生些什麼事情。
首先,撥朋友的號碼來發起呼叫。朋友應答,會說“嗨”之類的話,然後我們回應:“嗨,Lucy。我是 Mike。”我們和朋友聊一會兒,然後互說再見並掛機。這是面向連接協議中發生的典型狀況。在連接建立階段,一端與其對等實體聯系,交換初始問候信息,對會話中要用到的一些參數和選項進行溝通,然後連接進入數據傳輸階段。
在電話交談的過程中,兩端用戶都知道他們在和誰說話,因此沒必要不停地說“這是 Mike 在跟 Lucy 說話”。也沒必要在每次說話之前都撥一次朋友的電話號碼——我們的電話已經連接起來了。同理,在面向連接協議的數據傳輸階段,也沒必要說明我們自己或對等實體的地址。連接為我們維護的狀態中包含了這些地址。我們只要發送數據就行了,不需要考慮尋址或其他與協議相關的問題。
就像用電話交談一樣,連接的任一端完成數據的傳輸時,都要通知其對等實體。兩端都完成傳輸時,要依次將連接拆除。
既然無連接協議有這麼多的缺點,大家可能會奇怪,為什麼還要使用這種協議呢?我們會看到,在很多情況下,使用無連接協議構建應用程序都是有意義的。比如,使用無連接協議可以很方便地支持一對多和多對一通信,而面向連接協議通常都需要多個獨立的連接才能做到。但更重要的是,無連接協議是構建面向連接協議的基礎。TCP/IP 是基於一個4層的協議棧,如下圖所示:
如圖所示,TCP 和 UDP 都是構建在 IP 之上的。因此,IP 是構建整個 TCP/IP 協議族的基礎。但 IP 提供的是一種盡力而為的、不可靠的無連接服務。它接收來自其上層的分組,將它們封裝在一個 IP 分組中,根據路由為分組選擇正確的硬件接口,從這個接口將分組發送出去。一旦將分組發送出去了,IP 就不再關心這個分組了。和所有無連接協議一樣,它將分組發送出去之後就不再記得這個分組了。
這種簡單性也是 IP 的主要優點。因為它對底層的物理介質沒有作任何假設,所以在任何能夠承載分組的物理鏈路上都可以運行 IP。例如,IP 可以運行在簡單的串行鏈路、以太網和令牌環 LAN、X.25 和使用 ATM(Asychronous Transfer Mode,異步轉移模式)的 WAN、CDPD(Cellular Digital Packet Data,無線蜂窩數字分組數據)網,以及很多其他網絡上。盡管這些網絡技術之間有很大的差異,但 IP 對它們一視同仁,除了認為它們可以轉發分組之外沒有對其作任何假設。這種機制隱含了很深的意義。IP 可以運行在任何能夠承載分組的網絡上,所以整個 TCP/IP 協議族也可以。
現在我們來看看 TCP 是怎樣利用這種簡單的無連接服務來提供可靠的面向連接服務的。TCP 的分組被稱為段(segment),是放在 IP 數據報中發送的,因此,根本無法假定這些分組會抵達目的地,更不用說保證分組無損壞且以原來的順序到達了。
為了提供這種可靠性,TCP 向基本的 IP 服務中添加了三項功能:
首先,它為 TCP 段中的數據提供了校驗和。這樣有助於確保抵達目的地的數據在傳輸過程中不會被網絡損壞。
第二,它為每字節分配了一個序列號,這樣,如果數據抵達目的地時真的錯序了,接收端也能夠按照恰當的順序將其重裝起來。當然,TCP 並沒有為每字節都附加一個序列號。實際上,每個 TCP 段的首部都包含了段中第一字節的序列號。這樣,就隱含地知道了段中其他字節的序列號。
第三,TCP 提供了一種確認-重傳機制,以確保最終每個段都會被傳送出去。
另一方面,UDP 為編寫應用程序的程序員提供了一種不可靠的無連接服務。事實上,UDP 只向底層的 IP 協議中添加了兩項功能。
首先,它提供了一個可選的校驗和來檢測數據的損壞情況。盡管 IP 也有校驗和,但它只對 IP 分組首部進行計算,所以,TCP 和 UDP 也都提供了校驗和來保護它們自己的首部和數據。
其次,UDP 向 IP添加的第二項特性就是端口的概念。
回到與電話/寄信的類比中來,我們可以把 TCP 連接中的網絡地址當作一個辦公室總機的電話號碼,把端口號當作辦公室中某台正被呼叫的特定電話的分機號。同理,可以將UDP網絡地址當作一座公寓樓的地址,並把端口號當作公寓樓大廳中的個人郵箱。