[C#] 撰寫非同步方法 TCP socket #1

URL Link //n.sfs.tw/11625

2017-08-14 21:15:21 By 張○○

此文分為二個部分

[C#] 撰寫非同步方法 TCP socket #1

[C#] 撰寫非同步方法 TCP socket #2

一、前言

在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()
從已繫結的 Socket 接收資料
 

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 本身是一個介面,即只有定義沒有實作項的類別,他有幾個屬性,我們以後會用到:

介面不是本文的重點,所以就先略過。接下來實作 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 間,從論文的一部分整理出來發佈給需要的人參考