---------------------------------------------------------------
一、什么是多路復用和多路分解
我們知道,在網絡上主機與主機之間的通信,實質上是主機上運行的應用進程之間的通信。例如,當你通過Http上網瀏覽網頁時,實質上是你所訪問的主機的服務器進程與你本機的瀏覽器進程在進行通信。試想一下,當你在上網的同時,還掛着QQ,還使用ftp下載大文件,這時就有三個網絡上的進程與你的主機上的三個進程進行通信,那么系統是怎么樣正確地把接收到的數據定位到指定的進程中的呢?也就是說,系統是怎么把從ftp服務器發送過來的數據交付到ftp客戶端,而不把這些數據交付到你的QQ上的呢?反過來考慮,系統又是如何精確地把來自各個應用進程的數據發到網絡上指定上的主機(服務器)上的對應進程的呢?這就是多路分解與多路復用的作用了。
為了說明這個問題,先來補充一下操作系統方面的知識,以Linux對文件和設備的管理和使用方式為例。
為了方便資源的使用,提高機器的性能、利用率和穩定性等等原因,我們的計算機都有一層軟件叫做操作系統,它用於幫我們管理計算機可以使用的資源,當我們的程序要使用一個資源的時候,可以向操作系統申請,再由操作系統為我們的程序分配和管理資源。通常當我們要訪問一個內核設備或文件時,程序可以調用系統函數,系統就會為我們打開設備或文件,然后返回一個文件描述符fd(或稱為ID,是一個整數),我們要訪問該設備或文件,只能通過該文件描述符。可以認為該編號對應着打開的文件或設備。
而當我們的程序要使用網絡時,要使用到對應的操作系統內核的操作和網卡設備,所以我們可以向操作系統申請,然后系統會為我們創建一個套接字Socket,並返回這個Socket的ID,以后我們的程序要使用網絡資源,只要向這個Socket的編號ID操作即可。而我們的每一個網絡通信的進程至少對應着一個Socket。向Socket的ID中寫數據,相當於向網絡發送數據,向Socket中讀數據,相當於接收數據。而且這些套接字都有唯一標識符——端口號。
有了上面的了解后,再來說說什么是多路分解和多路復用。
每個運輸層的報文段中設置了幾個字段,包括源端口號和目的端口號等。多路分解就是,在接收端,運輸層檢查這些字段並標識出接收套接字,然后將該報文定向到該套接字。其工作方式可以簡單地認為是這樣的,主機上的每個每個套接字被分配一個端口號,當報文到達主機時,運輸層檢查報文段中的目的端口號,並將其定向到相應的套接字。
多路復用就是從源主機的不同套接字中收集數據塊,並為每個數據塊封裝上首部信息從而生成報文段,然后將報文段傳遞到網絡層中去。
二、無連接的多路復用和多路分解
在運輸層,無連接的網絡傳輸是通過UDP來實現的。UDP報文中只有源端口號和目的端口號,一個UDP套接字是由一個含有目的IP地址和目的端口號的二元組來全面標識的。在客戶端,源端口號是客戶進程套接字的端口號,目的端口號是服務器的端口號。而在服務器端,源端口號是服務器的創建的套接字的端口號,而目的端口號是客戶端的套接字的端口號。
例如主機A產生了一個UDP報文段,報文段中就會包括源端口號(11111)、目的端口號(22222)、程序數據(還有兩個其他的值,在這里我們不關心)。然后,運輸層將生成的報文段交給網絡層。網絡層將其放到一個IP數據報中,並提供盡力而為的交付,將其發送到主機B中。如果該報文到達主機B,主機B運輸層就會檢查該報文的端口號,並將該報文段傳遞給套接字的端口號為接收到的報文段的目的端口號(22222)的套接字。從而實現了進程間的網絡通信。而源端口號的作用是為了讓主機B能向主機A發送信息的,也就是說,當主機B在接收到主機A的數據后,要向主機A發送一個回應時,主機B發送的報文段的目的端口號就是11111.
注意:我們看到使用UDP來傳輸報文段時,一個UDP套接字是由一個含有目的IP地址和目的端口號的二元組來全面標識的。因此,如果兩個UDP報文段有不同的源IP地址和源端口,但具有相同的IP地址和目的端口號,那么這兩個報文段將通過相同的目的端口號定向到相同的目的進程。這里沒有過多地說明IP地址,是因為IP地址是網絡層的知識,所以沒有提及,我們現在只須知道,IP地址對應着一台主機,而端口號對應着一台主機上的一個進程(或套接字)。
三、面向連接的多路復用和多路分解
從上面的解說中,我們可以知道,網絡上主機間的進程間通信,實質上是通過套接字來實現的。在運輸層中面向連接的網絡傳輸多使用TCP,而TCP套接字和UDP套接字之間有一個細微的差別,就是,TCP套接字是由一個四元組(源IP地址、源端口號,目的IP地址,目的端口號)來標識的。這樣,當一個TCP報文段從網絡到達一台主機時,主機會使用全部4個值來將報文段定向,即多路分解到相應的套接字。
與UDP不同的是,兩個具有不同源IP或源端口號的到達的TCP報文段將被重定向到兩個不同的套接字。
盡管如此,而TCP的多路利用和多路分解的工作原理與無連接的UDP的多路復用和多路分解的原理還是大致一樣的。
想想為什么TCP的多路復用和多路分解要這樣設計呢?個人認為,這是因為TCP和UDP對待接收到的數據的處理方式不同所致的。我們以服務器上的TCP套接字和UDP套接字為例,假定服務器接收客戶端的數據,並把數據發送回客戶端。
當一個UDP服務器接收到一個UDP報文段時,它會根據收到的UDP報文段的源IP和源端口號,把數據發送回客戶端,它並不需要創建一個新的套接字來處理該報文段;
而對於一個TCP服務器,當它接受一個連接時,它會產生一個新的套接字,然后通過新的套接字來與客戶端通信,也就是通過新的套接字來把數據發送回給客戶端。由於每一個連接都會產生一個新的套接字,所以具有不同的源IP或源端口號的連接就是一個不同的連接,對應着產生的新的不同的套接字。
試想一下,如果TCP套接字也是使用像UDP那樣的只用源端口號和目的端口號來完全標識一個套接字,那么當客戶機A有一個Http連接時,該TCP報文的目的端口號為80,目的IP地址為TCP服務的IP地址。TCP服務器產生一個新的套接字來處理該請求,此時,客戶機B又有一個Http連接,TCP報文的目的端口號也為80,目的IP地址也為TCP服務的IP地址。而TCP套接字也是使用像UDP那樣,兩個具有不同的源Ip或源端口號但具有相同目的IP和目的端口的報文段定位到同一個套接字中,那么這個客戶機B的TCP報文段則會多路分解到客戶機A的套接字上,而該套接字並不應該被客戶機B的Http連接使用。
PS:如果對於這個解說不太明白,可以看看本人寫的一個用TCP和UDP進行通信的小例子,