當Server 接受 Client 的連線後,有指定當該Socket有資料傳入時,要叫用接受資料WaitForData()函式,其 WaitForData() 詳細如下:
//宣告AsyncCallback類別的變數 pfnWorkerCallBack
public AsyncCallback pfnWorkerCallBack;
public void WaitForData(System.Net.Sockets.Socket soc)
{
try{
//當pfnWorkerCallBack物件尚未實體化時,進行實體化
if (pfnWorkerCallBack == null)
{
//假設已連線的RCC客戶端要傳送資料給RCS時,所指定的回呼函式-OnDataReceive,這也是C#中的delagate型別,此函式內容詳述於後
pfnWorkerCallBack = new AsyncCallback(OnDataReceived);
}
//自行定義的型別 SocketPacket,附於此小節尾,內容只有一個Socket類和一個int。
SocketPacket theSocPkt = new SocketPacket();
//指定此一建立連線之Socket soc 給定義的 theSocPkt
theSocPkt.m_currentSocket = soc;
// 接下來定義當Socket 接收到資料時要交給哪一個函數處理,這裡定義了BeginReceive()
// 參數第一個為存放的位置,為byte[],傳入的資料會置於此陣列中;第二參數為啟始位置、第三為一次置入的長度;第四為封包的旗標,例如標記為廣播封包 之類,第五參數為開始接受資料時叫用的回呼函式,由於pfnWorkerCallBack已實體化指定OnDataReceived為回呼函式,所以當有 客戶端將資料傳進來時,OnDataReceived函式會被叫用;第六為狀態的參數,這個是用戶自訂,當回呼函式OnDataReceived被叫用 時,此參數的值會被傳遞到OnDataReceived,因此雖然SocketPacket theSocPkt宣告的是區域變數,但是回呼函式仍會曉得是哪一個Socket在傳送資料。
soc.BeginReceive(theSocPkt.dataBuffer, 0, theSocPkt.dataBuffer.Length, SocketFlags.None, pfnWorkerCallBack, theSocPkt);
}
catch (SocketException) { …例外處理略... }
}
//自型定義的物件封包的類別
public class SocketPacket
{
//目前Activating之Socket
public System.Net.Sockets.Socket m_currentSocket;
public byte[] dataBuffer = new byte[1500]; //接受資料陣列
}
當建立連線的 Socket 有資料傳入時,要叫用的回呼函式為OnDataReceived() 。
此回呼函式有一參數類型為IasyncResult,因為該類型是.NET中很特別的類型,稱之為介面(Interface),簡單的說,介面算是一個只有定義沒有實作項的類別。由於BeginReceive 方法被叫用時的傳入的最未個參數是自定的theSocPkt物件,會轉型成IAsyncResult.AsyncState 傳入,因此宣告的IasyncResult類變數asyn,則asyn.AsyncState 可取得符合或包含非同步作業資訊的使用者定義的物件。
下方將實作出OnDataReceived() 函式,由於此函式必需將收到的資料分析及編碼處理,所以原函式稍長,故僅節錄出和TCP非同步連線有關之部分於下:
public void OnDataReceived(IAsyncResult asyn)
{
try{
SocketPacket socketData = (SocketPacket)asyn.AsyncState;//取得接受的資料
string msg = "";//宣告及定義訊息字串
int iRx = 0;//宣告及定義訊息長度
//叫用 EndReceive 完成指定的非同步接收作業, 參數asyn 識別要完成的非同步接收作業,並要從其中擷取最終結果。
iRx = socketData.m_currentSocket.EndReceive(asyn);
//以下為將取出的部分轉成 UTF8的編碼方式,有關編碼方式轉換可依需求自行修改
byte[] databuff = socketData.dataBuffer;
msg = Encoding.UTF8.GetString(databuff);
…分析處理部分略,主要msg訊息的利用和重組…
//重新叫用 WaitForData 重新開始接收資料
WaitForData(socketData.m_currentSocket);
}
catch (SocketException) { …例外處理略... }
}
以上幾個 函式就完成了 非同步方法 TCP socket ,這裡將其整理一下:
Server() 啟動監聽
OnClientConnect(IAsyncResult) 當客戶端建立連線時叫用
WaitForData(Socket) 建立連線等待資料傳入
OnDataReceived(IAsyncResult) 已連線連線並有資料傳入時叫用
FindEmptyChannel() 尋找未用的頻道
祝各位使用愉快
[參考資料]
Asynchronous Socket Programming in C#
http://www.codeguru.com/csharp/csharp/cs_network/sockets/article.php/c7695/#more