1. 程式人生 > >TCP/IP 應用程式的通訊連線模式

TCP/IP 應用程式的通訊連線模式

TCP/IP 應用層與應用程式

TCP/IP 起源於二十世紀 60 年代末美國政府資助的一個分組交換網路研究專案,它是一個真正的開放協議,很多不同廠家生產各種型號的計算機,它們執行完全不同的作業系統,但 TCP/IP 協議元件允許它們互相進行通訊。現在 TCP/IP 已經從一個只供一些科學家使用的小實驗網成長為一個由成千上萬的計算機和使用者構成的全球化網路,TCP/IP 也已成為全球因特網(Internet)的基礎,越來越多的 TCP/IP 網際網路應用和企業商業應用正在改變著世界。

TCP/IP 通訊協議採用了四層的層級模型結構(注:這與 OSI 七層模型不相同),每一層都呼叫它的下一層所提供的網路任務來完成自己的需求。TCP/IP 的每一層都是由一系列協議來定義的。這 4 層分別為:

  • 應用層 (Application):應用層是個很廣泛的概念,有一些基本相同的系統級 TCP/IP 應用以及應用協議,也有許多的企業商業應用和網際網路應用。
  • 傳輸層 (Transport):傳輸層包括 UDP 和 TCP,UDP 幾乎不對報文進行檢查,而 TCP 提供傳輸保證。
  • 網路層 (Network):網路層協議由一系列協議組成,包括 ICMP、IGMP、RIP、OSPF、IP(v4,v6) 等。
  • 鏈路層 (Link):又稱為物理資料網路介面層,負責報文傳輸。

圖1顯示了 TCP/IP 層級模型結構,應用層之間的協議通過逐級呼叫傳輸層(Transport layer)、網路層(Network Layer)和物理資料鏈路層(Physical Data Link)而可以實現應用層的應用程式通訊互聯。

應用層需要關心應用程式的邏輯細節,而不是資料在網路中的傳輸活動。應用層其下三層則處理真正的通訊細節。在 Internet 整個發展過程中的所有思想和著重點都以一種稱為 RFC(Request For Comments)的文件格式存在。針對每一種特定的 TCP/IP 應用,有相應的 RFC 文件。一些典型的 TCP/IP 應用有 FTP、Telnet、SMTP、SNTP、REXEC、TFTP、LPD、SNMP、NFS、INETD 等。RFC 使一些基本相同的 TCP/IP 應用程式實現了標準化,從而使得不同廠家開發的應用程式可以互相通訊。

圖 1 TCP/IP 層級模型結構
圖 1 TCP/IP 層級模型結構

然而除了這些已經實現標準化的系統級 TCP/IP 應用程式外,在企業商業應用和網際網路應用開發中,存在著大量的商業應用程式通訊互聯問題。如圖 1 顯示,其中的應用層所包含應用程式主要可以分成兩類,即系統級應用和商業應用,網際網路商業應用是商業應用中的主要形式之一。

不同開發商和使用者在開發各自商業應用通訊程式時也存在有許多不同的設計方式。關於 TCP/IP 應用層以下的技術文獻與書籍早已是汗牛充棟,但是關於 TCP/IP 應用本身,尤其是關於商業應用的通訊設計模式技術討論方面的文章還是比較少的。TCP/IP 應用通訊設計模式實際上是在 TCP/IP 基礎程式設計之上的一種應用程式設計設計方式,也屬於一種應用層協議範疇,其可以包含有 TCP/IP 地址族模式設計、I/O 模式設計、通訊連線模式設計以及通訊資料格式設計等。鑑於目前討論 TCP/IP 商業應用程式設計模式問題這方面的文章還很少見,本文嘗試給出一些通訊連線模式設計中共同的概念與一些典型的設計模式,在以後的文章中將繼續討論地址族模式設計、I/O 模式設計、以及通訊資料格式設計等方面的模式設計實現話題。

通訊連線模式設計主要考慮內容有:

  • 通訊兩端程式建立通訊方式
  • 通訊連線方式
  • 通訊報文傳送與接收方式

以下內容將介紹建立通訊的 Client/Server 模型,然後逐一介紹通訊連線模式設計所需要考慮的這些內容。

傳輸層介面 APIs 與 TCP/IP 應用程式 C/S 模型

傳輸層介面 APIs

TCP/IP 應用層位於傳輸層之上,TCP/IP 應用程式需要呼叫傳輸層的接口才能實現應用程式之間通訊。目前使用最廣泛的傳輸層的應用程式設計介面是套接字介面(Socket)。Socket APIs 是於 1983 年在 Berkeley Socket Distribution (BSD) Unix 中引進的。 1986 年 AT&T 公司引進了另一種不同的網路層程式設計介面 TLI(Transport Layer Interface),1988 年 AT&T 釋出了一種修改版的 TLI,叫做 XTI(X/open Transport interface)。XTI/TLI 和 Socket 是用來處理相同任務的不同方法。關於 TCP/IP APIs 使用文章與書籍已相當多,本文則是側重於如何組合使用這些 APIs 來進行 TCP/IP 應用程式連線模式設計,並歸納出幾種基本應用連線模式。

如圖 2 顯示,應用層是通過呼叫傳輸層介面 APIs(Socket 或 XTI/TLI)來與傳輸層和網路層進行通訊的。

圖 2 傳輸層介面
圖 2 傳輸層介面

不管是使用何種程式設計介面,要在兩個機器或兩個程式之間建立通訊,通訊雙方必須建立互相一致的通訊模式。如果雙方的通訊設計模式不一致就無法建立有效的通訊連線。

以下是經常使用的 socket APIs,是建立 TCP/IP 應用程式的標準介面,也是影響 TCP/IP 應用程式通訊方式的幾個主要 APIs,不同 APIs 組合再結合系統呼叫可以實現不同方式的應用。Sockets 支援多種傳輸層和網路層協議,支援面向連線和無連線的資料傳輸,允許應用分散式工作。

  • socket():是用來建立一個 socket,socket 表示通訊中的一個節點,其可以在一個網路中被命名,用 socket 描述符表示,socket 描述符類似於 Unix 中的檔案描述符。
  • bind():是用來把本地 IP 層地址和 TCP 層埠賦予 socket。
  • listen() :把未連線的 socket 轉化成一個等待可連線的 socket,允許該 socket 可以被請求連線,並指定該 socket 允許的最大連線數。
  • accept():是等待一個連線的進入,連線成功後,產生一個新的 socket 描述符,這個新的描述符用來建立與客戶端的連線。
  • connect():用來建立一個與服務端的連線。
  • send():傳送一個數據緩衝區,類似 Unix 的檔案函式 write()。另外 sendto() 是用在無連線的 UDP 程式中,用來發送自帶定址資訊的資料包。
  • recv():接收一個數據緩衝區,類似 Unix 的檔案函式 readI()。另外 recvfrom() 是用在無連線的 UDP 程式中,用來接收自帶定址資訊的資料包。
  • close():關閉一個連線

Client/Server 模型

Sockets 是以 Client 和 Server 互動通訊方式來使用的。典型的系統配置是把 Server 放在一臺機器中,而把 Client 放在另一臺機器中,Client 連線到 Server 交換資訊。一個 socket 有一系列典型的事件流。例如,在面向連線的 Client/Server 模型中,Server 端的 socket 總是等待一個 Client 端的請求。要實現這個請求,Server 端首先需要建立能夠被 Client 使用的地址,當地址建立後,Server 等待 Client 請求服務。當一個 Client 通過 socket 連線到 Server 後,Client 與 Server 之間就可以進行資訊交換。Client/Server 是通訊程式設計的基本模式。從軟體開發的角度講,TCP/IP 應用程式都是基於 Client/Server 方式的。注意本篇文章以下 Client/Server 概念是針對程式內部呼叫 Socket API 所講的概念,與針對整個程式甚至針對機器而講的客戶端 / 伺服器概念有所不同。用 Server APIs 建立的程式可以被當作客戶端使用,用 Client APIs 建立的程式也可以被用作伺服器端使用。建立 Server 需要的 APIs 有 socket(), bind(), listen(), accept(),建立 Client 需要的 APIs 有 Socket(), Connect()。在實際應用開發中,同一個程式裡往往同時可以有 Client 和 Server 的程式碼,或者多種形式的組合。在實際應用程式設計中,針對 Socket APIs 不同有效組合,結合系統呼叫可以有多種複雜的設計變化。

面向連線的應用程式設計存在三類基本的不同級別的設計方式範疇,根據 Socket APIs 從上到下順序依次是:

  • Client/Server 通訊建立方式
  • Client/Server 通訊連線方式
  • Client/Server 通訊傳送與接收方式

下面內容以面向連線的 Socket 應用程式設計為例來說明這幾種不同通訊範疇的設計實現。

Client/Server 建立方式設計概述

一個 Client 連線一個 Server

如果只有兩臺機器之間連線,那麼一個是 Client,另一個是 Server,如下面圖 3 所示。這是最簡單的 TCP/IP 的應用,也是 TCP/IP 應用早期的 Peer to Peer (P2P) 概念。其流程基本如圖 4 所示。

圖 3 TCP/IP 應用單點 Client/Server
圖 3 TCP/IP 應用單點 Client/Server

圖 4 顯示了 TCP/IP 應用程式設計最基本的 Client/Server 模式,顯示了基本的 Client/Server 通訊所需要呼叫的 Socket APIs 以及順序。

圖 4 TCP/IP 應用程式設計基本 Client/Server 模式
圖 4 TCP/IP 應用程式設計基本 Client/Server 模式

多個 Client 連線一個 Server

多個 Client 同時連線一個 Server 是 TCP/IP 應用的主流形式,如圖 5 所示,其中 Client 連線數可以從幾個到成千上萬。

圖 5 TCP/IP 應用多 Client 端的 Client/Server
圖 5 TCP/IP 應用多 Client 端的 Client/Server

由於 socket APIs 預設方式下都是阻塞方式的,實現多個 Client 同時連線一個 Server 就需要特別的設計。其實現方式可以有多種不同的設計,這其中也涉及 I/O 模式設計。下面將展開介紹其中幾種設計形式。

利用一個 Client 連線一個 Server 形式實現多 Client 連線

從程式設計角度講,只要 Client 和 Server 埠是一對一形式,那麼就屬於一個 Client 連線一個 Server 形式。在處理多個 Client 端連線時,Server 端輪流使用多個埠建立多個 Client-Server 連線,連線關閉後,被釋放埠可以被迴圈使用。在這種多連線形式中需要謹慎處理 Client 端如何獲取使用 Server 端的可用埠。比如圖 6 顯示 Server 有一個服務於所有程序的程序可以先把 Server 端的可用埠傳送給 Client 端,Client 端再使用該埠建立連線來處理業務。Server 針對每一個 Client 連線用一個專門的程序來處理。由於可用埠數有限,Server 用一個有限迴圈來處理每一個可用的埠連線。由於新埠需要用 bind() 來繫結,所以需要從 bind() 開始到 close() 結束都需要包含在迴圈體內。

圖 6 利用一對一 Client-Server 模式實現多 Client 連線
圖 6 利用一對一 Client-Server 模式實現多 Client 連線

使用多個 accept() 實現多 Client 連線

多程序 Server 一般有一個專注程序是服務於每一個連線的。當 Client 端完成連線後,專注程序可以迴圈被另外的連線使用。使用多個 accept() 也可以實現處理多 Client 連線。多 accept() 的 Server 也只有一個 socket(),一個 bind(),一個 listen(),這與通常情況一樣。但是它建立許多工作子程序,每一個工作子程序都有 accept(),這樣可以為每一個 Client 建立 socket 描述符。如圖 7 所示,由於 accept() 連線成功後,會產生一個新的 socket 描述符,這樣通過迴圈多程序利用 accept() 產生的多 socket 描述符就可以與多個 Client 進行連線通訊。迴圈體是從 accept() 開始到 close() 結束的。

圖 7 使用多 accept() 實現多 Client 連線
圖 7 使用多 accept() 實現多 Client 連線

使用併發 Server 模式實現多 Client 連線

併發伺服器模式曾經是 TCP/IP 的主流應用程式設計模式,得到廣泛使用,目前網際網路上仍有相當多的應用使用此種模式。其設計思路是在 accept 之後 fork 出一個子程序。因為 socket 會產生監聽 socket 描述符 listenfd,accept 會產生連線 socket 描述符 connfd。連線建立後,子程序繼承連線描述符服務於 Client,父程序則繼續使用監聽描述符等待另外一個 Client 的連線請求,以產生另外一個連線 socket 描述符和子程序。如圖 8 所示,accept() 接收到一個 Client 連線後,產生一個新的 socket 描述符,通過 fork() 系統呼叫,用一個子程序來處理該 socket 描述符的連線服務。而父程序可以立即返回到 accept(),等待一個新的 Client 請求,這就是典型的併發伺服器模式。併發伺服器模式同時處理的最大併發 Client 連線數由 listen() 的第二個引數來指定。

圖 8 TCP/IP 應用併發 Server
圖 8 TCP/IP 應用併發 Server

使用 I/O 多路技術實現多 Client 連線

以上三種連線設計,多 Server 埠、多 accept() 和併發伺服器模式,都是通過 fork() 系統呼叫產生多程序來實現多 Client 連線的。使用 I/O 多路技術也可以同時處理多個輸入與輸出問題,即用一個程序同時處理多個檔案描述符。I/O 多路技術是通過 select() 或 poll() 系統呼叫實現的。poll() 與 select() 功能完全相同,但是 poll() 可以更少使用記憶體資源以及有更少的錯誤發生。select() 呼叫需要與操作檔案描述符集的 APIs 配合使用。select() 系統呼叫可以使一個程序檢測多個等待的 I/O 是否準備好,當沒有裝置準備好時,select() 處於阻塞狀態中,其中任一裝置準備好後,select() 函式返回呼叫。select() API 本身也有一個超時時間引數,超時時間到後,無論是否有裝置準備好,都返回呼叫。其流程如圖 9 所示。在 socket APIs listen() 和 accept() 之間插入 select() 呼叫。使用這三個巨集 FD_ZERO()、FD_CLR() 和 FD_SET(),在呼叫 select() 前設定 socket 描述符遮蔽位,在呼叫 select() 後使用 FD_ISSET 來檢測 socket 描述符集中對應於 socket 描述符的位是否被設定。 FD_ISSET() 就相當通知了一個 socket 描述符是否可以被使用,如果該 socket 描述符可用,則可對該 socket 描述符進行讀寫通訊操作。通常,作業系統通過巨集 FD_SETSIZE 來宣告在一個程序中 select() 所能操作的檔案或 socket 描述符的最大數目。更詳細的 I/O 多路技術實現,可以參考其他相關文獻。

圖 9 I/O 多路技術實現多連線的 Server
圖 9 I/O 多路技術實現多連線的 Server

一個 Client 連線多個 Server

一個 Client 連線多個 Server 這種方式很少見,主要用於一個客戶需要向多個伺服器傳送請求情況,比如一個 Client 端掃描連線多個 Server 端情況。如圖 10 所示。此種方式設計主要是 Client 端應用程式的邏輯設計,通常需要在 Client 端設計邏輯迴圈來連線多個 Server,在此不做更多描述。

圖 10 單 Client 對多 Server
圖 10 單 Client 對多 Server

複雜 Client/Server 設計與現代 P2P

最近幾年,對等網路技術 ( Peer-to-Peer,簡稱 P2P) 迅速成為計算機界關注的熱門話題之一,以及影響 Internet 未來的科技之一。與早期點對點 (Peer to Peer) 的 Client/Server 模式不同,現在的 P2P 模式是指每個結點既可充當伺服器,為其他結點提供服務,同時也可作為客戶端享用其他結點提供的服務。實際上 P2P 模式仍然是基於 Client/Server 模式的,每個通訊節點都既是 Server,又是 Client,P2P 是基於複雜 Client/Server 設計的 TCP/IP 應用。圖 11 顯示 P2P 模式下兩個使用者 PC 之間的對等連線。

圖 11 P2P 模式
圖 11 P2P 模式

在技術上,P2P 本身是基於 TCP/IP Client/Server 技術的一種設計模式思想, P2P 也屬於網路應用層技術,與 Web 和 FTP 等應用是並列的。只是 P2P 應用在設計實現上更要複雜的多。P2P 技術實現的協同工作是無需專門的伺服器支援的 (Serverless),這裡的伺服器概念與 Client/Server 中的 Server 概念是不一樣的。在傳統意義上中心伺服器機器上往往執行的是 TCP/IP 應用的 Server 端程式,所以傳統意義上的 Server 概念在機器與應用上是重合的。如果更改 TCP/IP 的應用設計,使應用程式既可做 Server 又可做 Client,就可以實現無中心伺服器的 P2P 模式。

在設計模式上,P2P 模式實現了網路終端使用者不依賴中心伺服器或者服務商而直接進行資訊和資料交換的可能,因此 P2P 正在改變著整個網際網路的一些基礎應用,從而極大地增加了使用者之間的資訊溝通和交流能力。目前網際網路的 P2P 應用與網路都正在飛速發展,一些典型的 P2P 應用程式比如有 BitTorrent, eDonkey 等,另外一些即時通訊(IM)類軟體比如 MSN、QQ 等也正在向無中心伺服器模式轉變。無中心伺服器的 Internet 應用程式大大降低應用提供商的運營成本,而且減少人們對於 Server 穩定性的依賴。

Client/Server 通訊連線方式設計

Client/Server 通訊方式建立後,下一步就需要考慮通訊連線的方式,主要有兩種方式的連線,即長連線通訊與短連線通訊。通訊連線方式涉及到的 APIs 主要是 connect() 和 accept()。要實現某種 Client/Server 方式,就必須考慮用某種特定的連線方式。

短連線通訊

短連線通訊是指 Client 方與 Server 方每進行一次通訊報文收發交易時才進行通訊連線,交易完畢後立即斷開連線。此種方式常用於多個 Client 連線一個 Server 情況,常用於機構與使用者之間通訊,比如 OLTP(聯機事務處理)類應用。在短連線情況下,Client 端完成任務後,就關閉連線並退出。在 Server 端,可以通過迴圈 accept(),使 Server 不會退出,並連續處理 Client 的請求。圖 12 顯示了一般情況下短連線通訊模式的 Socket 事件流,不同設計的連線多 Client 的 Server 有不同的迴圈流程。

圖 12 短連線模式通訊
圖 12 短連線模式通訊

長連線通訊

長連線通訊是指 Client 方與 Server 方先建立通訊連線,連線建立後不會斷開,然後再進行報文傳送和接收,報文傳送與接收完畢後,原來連線不會斷開而繼續存在,因此可以連續進行交易報文的傳送與接收。這種方式下由於通訊連線一直存在,其 TCP/IP 狀態是 Established,可以用作業系統的命令 netstat 檢視連線是否建立。由於在長連線情況下,Client 端和 Server 端一樣可以固定使用一個埠,所以長連線下的 Client 也需要使用 bind() 來繫結 Client 的埠。在長連線方式下,需要迴圈讀寫通訊資料。為了區分每一次交易的通訊資料,每一次交易資料常常需要在資料頭部指定該次交易的長度,接收 API 需要首先讀出該長度,然後再按該長度讀出指定長度的位元組。長連線方式常用於一個 Client 端對一個 Server 端的通訊,一般常用於機構與機構之間的商業應用通訊,以處理機構之間連續的大量的資訊資料交換。或者說可用於兩個系統之間持續的資訊交流情況。通常為了加快兩個系統之間的資訊交流,通常還需要建立幾條長連線的並行通訊線路。圖 13 顯示了一般情況下長連線通訊模式的 socket 事件流,可見其最大特點是 Client 和 Server 都有迴圈體,而且迴圈體只包含讀寫 APIs。

圖 13 長連線模式通訊
圖 13 長連線模式通訊

Client/Server 通訊傳送與接收方式設計

在通訊資料傳送與接收之間也存在不同的方式,即同步和非同步兩種方式。這裡的同步和非同步與 I/O 層次的同異步概念不同。主要涉及 socket APIs recv() 和 send() 的不同組合方式。

同步傳送與接收

從應用程式設計的角度講,報文傳送和接收是同步進行的,既報文傳送後,傳送方等待接收方返回訊息報文。同步方式一般需要考慮超時問題,即報文發出去後傳送方不能無限等待,需要設定超時時間,超過該時間後傳送方不再處於等待狀態中,而直接被通知超時返回。同步傳送與接收經常與短連線通訊方式結合使用,稱為同步短連線通訊方式,其 socket 事件流程可如上面的圖 12 所示。

非同步傳送與接收

從應用程式設計的角度講,傳送方只管傳送資料,不需要等待接收任何返回資料,而接收方只管接收資料,這就是應用層的非同步傳送與接收方式。要實現非同步方式,通常情況下報文傳送和接收是用兩個不同的程序來分別處理的,即傳送與接收是分開的,相互獨立的,互不影響。非同步傳送與接收經常與長連線通訊方式結合使用,稱為非同步長連線通訊方式。從應用邏輯角度講,這種方式又可分雙工和單工兩種情況。

非同步雙工

非同步雙工是指應用通訊的接收和傳送在同一個程式中,而有兩個不同的子程序分別負責傳送和接收,非同步雙工模式是比較複雜的一種通訊方式,有時候經常會出現在不同機構之間的兩套系統之間的通訊。比如銀行與銀行之間的資訊交流。它也可以適用在現代 P2P 程式中。如圖 14 所示,Server 和 Client 端分別 fork 出兩個子程序,形成兩對子程序之間的連線,兩個連線都是單向的,一個連線是用於傳送,另一個連線用於接收,這樣方式的連線就被稱為非同步雙工方式連線。

圖 14 長連線非同步雙工模式
圖 14 長連線非同步雙工模式

非同步單工

應用通訊的接收和傳送是用兩個不同的程式來完成,這種非同步是利用兩對不同程式依靠應用邏輯來實現的。圖 15 顯示了長連線方式下的非同步單工模式,在通訊的 A 和 B 端,分別有兩套 Server 和 Client 程式,B 端的 Client 連線 A 端的 Server,A 端的 Server 只負責接收 B 端 Client 傳送的報文。A 端的 Client 連線 B 端的 Server,A 端 Client 只負責向 B 端 Server 傳送報文。

圖 15 長連線非同步單工模式
圖 15 長連線非同步單工模式

典型通訊連線模式

綜上所述,在實際 TCP/IP 應用程式設計中,就連線模式而言,我們需要考慮 Client/Server 建立方式、Client/Server 連線方式、Client/Server 傳送與接收方式這三個不同級別的設計方式。實際 TCP/IP 應用程式連線模式可以是以上三類不同級別 Client/Server 方式的組合。比如一般 TCP/IP 相關書籍上提供的 TCP/IP 範例程式大都是同步短連線的 Client/Server 程式。有的組合是基本沒有實用價值的,比較常用的有價值的組合是以下幾種:

  • 同步短連線 Server/Client
  • 同步長連線 Server/Client
  • 非同步短連線 Server/Client
  • 非同步長連線雙工 Server/Client
  • 非同步長連線單工 Server/Client

其中非同步長連線雙工是較為複雜的一種通訊方式,有時候經常會出現在不同銀行或不同城市之間的兩套系統之間的通訊,比如國家金卡工程。由於這幾種通訊方式比較固定,所以可以預先編制這幾種通訊方式的模板程式。

轉至:http://www.ibm.com/developerworks/cn/aix/library/0807_liugb_tcpip/index.html?ca=drs