连接多个客户端的异步套接字服务器

问题描述:

我想用C#编写一个异步套接字服务器和客户端。服务器必须管理许多客户端连接,并尽可能保持它们的活动状态。 我尝试使用MSDN服务器代码from this site,但它无法同时处理多个客户端,并在发送确认消息后关闭连接。你能告诉我应该如何改变这个示例代码来管理同时向多个客户端发送消息(例如,在某个数组或列表中有客户端连接)并保持连接活动?连接多个客户端的异步套接字服务器

+1

我不认为这是一个坏问题,但问题是它是*太*开放式;这是一个巨大的主题,并且没有魔术棒可以让您轻松完成异步多客户端服务器的编写。你关于保持连接的问题在很大程度上取决于协议,因为真正做到这一点的唯一方法是偶尔发送某种消息(即使这样,套接字仍可能因任何原因被终止,但至少你会检测它,最迟在下一次错过的心跳) –

+0

作为一个例子,我有一个网络套接字(rfc6455等)服务器,它可以完成您所描述的任何事情:在单个计算器问题中尝试描述它太复杂了。 –

+0

好吧,让我们假设每隔半秒发送一次消息。我只想知道,如何连接到msdn网站上给出的客户端的服务器,并将消息发送到已连接的客户端(因为msdn的解决方案在消息发送后立即关闭连接) –

请注意,编写异步服务器需要比重写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