【问题标题】:Async socket server with multiple clients connected连接了多个客户端的异步套接字服务器
【发布时间】:2014-05-04 21:13:13
【问题描述】:

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

【问题讨论】:

  • 我不认为这是一个坏问题,但问题是它是开放式的;这是一个巨大的话题,没有什么魔杖可以让编写一个完全异步的多客户端服务器变得容易。您关于保持连接活动的问题很大程度上取决于协议,因为真正做到这一点的唯一方法是偶尔发送某种消息(即使那样,套接字仍然可能因任何原因而终止,但至少你会最迟在下一次错过的心跳中检测到它)
  • 例如,我有一个 web-socket(rfc6455 等)服务器,可以完成您描述的所有事情:尝试在单个 stackoverflow 问题中描述它太复杂了。
  • 好吧,假设我每半秒发送一次消息。我只是想知道,如何使用 msdn 站点上给出的客户端连接到服务器并向已连接的客户端发送消息(因为 msdn 的解决方案在发送消息后不久关闭连接)

标签: c# sockets asyncsocket


【解决方案1】:

请注意,编写异步服务器比重写 MSDN 示例需要更多的工作(我目前正在编写处理 4-5000 个同时连接的异步服务器,这不是一项简单的任务!)。

话虽如此,该示例似乎完全能够处理多个客户端;但是,只要您不管理连接,您就无法向客户端发送任何消息,也无法以优雅的方式关闭服务器(在关闭之前断开所有客户端的连接)。

如果您只需要向所有客户端广播消息,您可以轻松地使用所有已连接套接字的列表。 IE。在 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(Send),并等待对方回复关闭(在 BeginReceive 回调中将变为 0 字节),然后关闭连接。

这只是一个粗略的大纲,但还有很多很多事情需要解决。尤其是异常处理可能非常棘手!

为了使整个内容更具可读性,我首先将客户端和服务器部分拆分为单独的类 - 否则所有发送和读取方法很快就会变得无法区分。

【讨论】:

  • 我认为这就是我目前所需要的。非常感谢:)
  • 带有foreach循环的广播不是瓶颈吗?我的意思是如果有 5000 个客户端连接,你必须等待 5000 个周期?
猜你喜欢
  • 2011-08-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多