此文分為二個部分
一、前言
在TCP連線中,如果是同步狀態,當SERVER LISTEN 一個埠時,整個程式就會停駐在此,等候用戶傳送資料進來,如果沒資料傳送,整個程式就好像是當掉一樣。當此時別的客戶想連線時,因為插座被佔了,除非 fork另一個程序(或是另開一個應用層的THREAD,否則別想連線,所以一般伺服器的SOCKET 都會以非同步的方法撰寫。
TCP連線主要有分為同步/非同步兩種方法(Synchronous/Asynchronous Methods) [1][2];在同步方法中,程式一次只能進行一個要求(request),並且會延遲其他的要求。例如當程式在等候資料傳送進來(Waiting),執行同步要求的執行緒因等候網路作業完成而無法再執行其他工作,包括使用者介面(UI)執行緒,那麼應用程式就會暫停並停止回應使用者的輸入。
此外,當此時別的客戶端想連線時,因為唯一的一個 Socket已被佔用,無法再接受其他的客戶連線。雖然使用同步的方法從觀念上來說比較容易使用,然而如果應用程式需要保持快速回應、提升延展性及可靠性及達到同時連線的目的,那麼就不應該使用同步封鎖的作業方式。
非同步方法的連線中,程式不會因為等候同步作業完成的執行緒而被封鎖。因為非同步作業(Asynchronous Operation)會在不同的執行緒上(在背景中)執行,所以應用程式可以在呼叫非同步方法(BeginOperationName)的執行緒上繼續執行。這樣的好處是有效率、高雅(不會因等待而暫停)並且能同時和許多個Clients建立連線,缺點是程式碼較不易撰寫。
在C#中,同步及非同步的連線的方法差異如下:
Synchronous Methods | Asynchronous Methods |
Connect() 建立至遠端主機的連接 |
BeginConnect() EndConnect() |
Receive() |
BeginReceive() 啟始非同步接收作業,方法是告知訊息佇列開始接收訊息,並在完成時告知事件處理常式。 EndReceive() 完成指定的非同步接收作業。 |
相對的,非同步的連線中,當Server已接受一個Clent的連線時,同時也能接收其他的Clients連線的請求。當其中一個Client傳送資料 時, 該插座Socket會啟動一個回呼CallBack事件,這個回呼事件會執行你所指定的函式(function),這在C#中叫作delegate,也就 是C++中的指標,而不同的是,在C#中是稱為「物件導向函式指標」,他指向一個函式,本文會對這方面作很多的說明,因為瞭解它是進入中高階程設的里程埤。
二、伺服器端程式
using System.Net; // 宣告Socket 類 mainSocket 為 class 變數 private Socket mainSocket; int port=12345; // 伺服函式 public function Server() { // 實體化 mainSocket mainSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //建立本機監聽位址及埠號,IPAddress.Any表示監聽所有的介面。 IPEndPoint ipLocal = new IPEndPoint(IPAddress.Any, port); //socket連繫到該位址 mainSocket.Bind(ipLocal); // 啟動監聽 //backlog=4 參數會指定可在佇列中等候接收的輸入連接數。若要決定可指定的最大連接數, 除非同時間的連線非常的大,否則值4應該很夠用。 mainSocket.Listen(4); // 以上完成了SERVER的Listening,BeginAccept() 開啟執行緒準備接收client的要求。 // 此處的BeginAccept 第一個參數就是當Socket一開始接收到Client的連線要求時, 立刻會呼叫delegate的OnClientConnect的方法,這個OnClientConnect名稱是我們自己取的, 你也可以叫用別的名稱。因此我們要另外寫一個OnClientConnect()的函式。 mainSocket.BeginAccept(new AsyncCallback(OnClientConnect), null); }
[灰色字是原理說明]
這裡我們先來看BeginAccept的說明:開始非同步作業以接受連入的連接嘗試。
public IAsyncResult BeginAccept (
AsyncCallback callback,
Object state
)
就 AsyncCallback 類別而言,他是一個代理人的函數。注意看到的delegate的型別,用淺紅色顯示:
public delegate void AsyncCallback (
IAsyncResult ar
)
值得注意的,在此AsyncCallback類別中,有一個參數是IAsyncResult ar表示非同步作業(Asynchronous Operation) 的狀態:public interface IAsyncResult
換句話說,在實作OnClientConnect 函數時,他必需引用一個IAsyncResult的參數。這裡的 IAsyncResult 本身是一個介面,即只有定義沒有實作項的類別,他有幾個屬性,我們以後會用到:
- AsyncState 取得符合或包含非同步作業資訊的使用者定義的物件。
- AsyncWaitHandle 取得 WaitHandle,用於等候非同步作業完成。
- CompletedSynchronously 取得非同步作業是否同步完成的指示。
- IsCompleted 取得非同步作業是否已完成的指示。
介面不是本文的重點,所以就先略過。接下來實作 CallBack 的函式。
要觸發 Server() 函式,可以拉一個按鈕,onclick 事件時呼叫他,或是在初始化時呼叫亦可,總之,Server() 執行後,伺服端程式已啟動監聽的工作。
更難的在這裡 [C#] 撰寫非同步方法 TCP socket #2
參考書目
[1] Beej's Guide to Network Programming Using Internet Sockets, Brian 「Beej」 Hall beej@beej.us Version 2.3.23, November 5, 2005
[2] David Makofske, Michael J. Donahoo, Kenneth L. Calvert TCP/IP Sockets in C#: Practical Guide for Programmers, The Practical Guides, Morgan Kaufmann 2004 .
原文 2010-04-02 00:52:54 撰於 97.7~8 間,從論文的一部分整理出來發佈給需要的人參考