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

bar*_*kmp 0 c# sockets asyncsocket

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

son*_*ard 7

请注意,编写异步服务器比重写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);
}
Run Code Online (Sandbox Code Playgroud)

如果您不熟悉服务器和套接字连接,这大致是事件的流程:

  1. 服务器创建一个侦听器,它将处理传入的连接,并提供回调

  2. 在侦听器上调用BeginAccept以指示服务器已准备好处理连接

  3. 当客户端连接时,侦听套接字回调:

    3A.在侦听器套接字上调用EndAccept提供了另一个进行实际通信的套接字.

    3B.要开始通信,请在通信套接字上调用BeginReceive,并提供回调来接收来自客户端的消息

    3C.再次调用BeginAccept以指示您已准备好接收新连接

  4. 收到消息后,通信套接字将回拨:

    4A.调用EndReceive以获取字节读取.如果字节数为0,则另一端正在断开连接

    4B.处理消息并调用BeginReceive以接收下一条消息

  5. 当你想要关闭连接时,发送一个Shutdown(发送),并等待另一方回复一个shutdown(在BeginReceive回调中将转为0字节),然后关闭连接.

这只是一个粗略的概述,但有很多很多东西需要解决.特别是异常处理可能真的很棘手!

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