2014-05-04 252 views
0

我想用C#編寫一個異步套接字服務器和客戶端。服務器必須管理許多客戶端連接,並儘可能保持它們的活動狀態。 我嘗試使用MSDN服務器代碼from this site,但它無法同時處理多個客戶端,並在發送確認消息後關閉連接。你能告訴我應該如何改變這個示例代碼來管理同時向多個客戶端發送消息(例如,在某個數組或列表中有客戶端連接)並保持連接活動?連接多個客戶端的異步套接字服務器

+1

我不認爲這是一個壞問題,但問題是它是*太*開放式;這是一個巨大的主題,並且沒有魔術棒可以讓您輕鬆完成異步多客戶端服務器的編寫。你關於保持連接的問題在很大程度上取決於協議,因爲真正做到這一點的唯一方法是偶爾發送某種消息(即使這樣,套接字仍可能因任何原因被終止,但至少你會檢測它,最遲在下一次錯過的心跳) –

+0

作爲一個例子,我有一個網絡套接字(rfc6455等)服務器,它可以完成您所描述的任何事情:在單個計算器問題中嘗試描述它太複雜了。 –

+0

好吧,讓我們假設每隔半秒發送一次消息。我只想知道,如何連接到msdn網站上給出的客戶端的服務器,並將消息發送到已連接的客戶端(因爲msdn的解決方案在消息發送後立即關閉連接) –

回答

4

請注意,編寫異步服務器需要比重寫MSDN示例更多的工作(我目前正在編寫一個處理4-5000個異步連接的異步服務器,並且這不是一項簡單的任務!)。

這就是說,樣本似乎完全有能力處理多個客戶;然而只要你不管理連接,你就不能發送任何消息給客戶端,也不能以優雅的方式關閉服務器(在關閉之前斷開所有客戶端)。

如果您只需要將消息廣播到所有客戶端,則可以輕鬆製作所有連接的套接字列表。即在AcceptCallback中,您應該將處理程序保存在列表中。爲了保持連接打開,請移除SendCallback中的handler.Shutdown()和handler.Close()。事情是這樣的:

private List<Socket> _clients = new List<Socket>(); 

public static void AcceptCallback(IAsyncResult ar) { 
    // Signal the main thread to continue. 
    allDone.Set(); 

    // Get the socket that handles the client request. 
    Socket listener = (Socket) ar.AsyncState; 
    Socket handler = listener.EndAccept(ar); 

    // Create the state object. 
    StateObject state = new StateObject(); 
    state.workSocket = handler; 
    handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, 
     new AsyncCallback(ReadCallback), state); 

    _clients.Add(handler); // Maintain connected clients 
} 

public void BroadcastMessage(string message) 
{ 
    // Send the message to all clients 
    var bytes = Encoding.ASCII.GetBytes(message); 
    foreach(var c in _clients) 
    { 
     c.BeginSend(bytes, 0, bytes.Length, SocketFlags.Broadcast, OnMessageBroadcast, c); 
    } 
} 

private void OnMessageBroadcast(IAsyncResult res) 
{ 
    Socket client = (Socket)res.AsyncState; 
    client.EndSend(res); 
} 

如果您不熟悉服務器和套接字連接,這是大致的事件流:

  1. 服務器創建一個偵聽器,它將處理傳入的連接,並提供在聽衆的回調

  2. 呼叫BeginAccept,表明服務器已準備好處理連接

  3. 當客戶端連接時,監聽套接字回撥:

    3a。在偵聽套接字上調用EndAccept提供了實際通信發生的另一個套接字。

    3b。要開始進行通信,來電來函插座上BeginReceive,並提供一個來自客戶端的郵件將被接收

    3C回調。再次調用BeginAccept以表明您已經準備好接受新的連接

  4. 當收到一個消息,通信插座回調:

    4A。調用EndReceive來讀取字節。如果字節計數爲0,另一方斷開連接

    4b。處理消息並調用BeginReceive接收下一條消息

  5. 當您想關閉連接時發送Shutdown(發送),並等待另一方回覆關閉(在BeginReceive回調中將變爲0字節),然後關閉連接。

這只是一個粗略的提綱,但有很多很多事情需要解決。尤其是異常處理可能非常棘手!

爲了使整個事情更具可讀性,我首先將客戶端和服務器部分拆分爲不同的類 - 否則所有發送和讀取方法都很難區分開來。

+0

我認爲這就是我所需要的遠。非常感謝:) –

+0

是不是廣播與foreach循環的瓶頸?我的意思是如果有5000個客戶端連接,你必須等待5000個週期? – hapablap