简体   繁体   English

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

[英]Async socket server with multiple clients connected

I want to write a asynchronous socket server and client in C#. 我想用C#编写一个异步套接字服务器和客户端。 The server has to manage many client connections and keep them alive as long as it's possible. 服务器必须管理许多客户端连接,并使其保持活动状态。 I tried using MSDN server code from this site , but it cant handle multiple clients at the same time and closes connection after sending acknowledge message. 我尝试从此站点使用MSDN服务器代码,但是它无法同时处理多个客户端,并且在发送确认消息后关闭连接。 Could you show me how I'm supposed to change that example code to manage sending messages to many clients at the same time (like, having client connections in some array or list) and keeping the connection alive? 您能告诉我如何更改该示例代码以管理同时向多个客户端发送消息(例如,将客户端连接置于某个数组或列表中)并使连接保持活动状态吗?

Be advised that writing an asynchronous server takes a lot more work than rewriting an MSDN sample (I am currently authoring an asynchronous server handling 4-5000 simulataneous connections, and it is not a trivial task!). 请注意,编写异步服务器要比重写MSDN示例花费更多的工作(我目前正在编写处理4-5000个同时连接的异步服务器,这不是一件容易的事!)。

That being said, the sample seems be perfectly capable of handling multiple clients; 话虽如此,该样本似乎完全能够处理多个客户。 however as long as you don't manage the connections, you would not be able to send any messages to the client, nor be able to shut down the server in a graceful manner (disconnecting all clients before shutting down). 但是,只要您不管理连接,就无法向客户端发送任何消息,也无法以正常的方式关闭服务器(在关闭之前断开所有客户端的连接)。

If all you need is to broadcast messages to all clients, you could easily make to with a list of all sockets connected. 如果只需要向所有客户端广播消息,则可以轻松列出所有连接的套接字。 Ie in the AcceptCallback, you should save the handler in a list. 即在AcceptCallback中,您应该将处理程序保存在列表中。 And to keep the connections open, remove the handler.Shutdown() and handler.Close() in SendCallback. 为了保持连接打开,请删除SendCallback中的handler.Shutdown()和handler.Close()。 Something like this: 像这样:

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);
}

In case you are unfamiliar with servers and socket connections, this is roughly the flow of events: 如果您不熟悉服务器和套接字连接,则大致是事件流:

  1. Server creates a listener, which will handle incoming connections, and provides a callback 服务器创建一个侦听器,该侦听器将处理传入的连接并提供回调

  2. Call BeginAccept on the listener to indicate that server is ready to handle connections 在侦听器上调用BeginAccept以指示服务器已准备好处理连接

  3. When a client connects, the listening sockets calls back: 当客户端连接时,侦听套接字会回调:

    3a. 3a。 Calling EndAccept on the listener socket provides another socket where the actual communication takes place. 在侦听器套接字上调用EndAccept提供了另一个套接字,在此发生实际的通信。

    3b. 3b。 To begin communication, call BeginReceive on the communication socket, and provide a callback where messages from the client will be received 要开始通信,请在通信套接字上调用BeginReceive,并提供一个回调,该回调将接收来自客户端的消息

    3c. 3c。 Call BeginAccept again to indicate that you are ready to receive new connection 再次调用BeginAccept表示您准备好接收新连接

  4. When a message is received, the communication socket calls back: 收到消息后,通信套接字将回叫:

    4a. 4a。 Call EndReceive to get bytes read. 调用EndReceive以读取字节。 If byte count is 0, the other side is disconnecting 如果字节数为0,则另一端正在断开连接

    4b. 4b。 Process the message and call BeginReceive to receive next message 处理消息并调用BeginReceive以接收下一条消息

  5. When you want to close the connection send a Shutdown(Send), and wait for the other side to reply with a shutdown (will turn up as 0 bytes in BeginReceive callback), then close the connection. 当您要关闭连接时,发送Shutdown(Send),然后等待另一端通过关闭响应(在BeginReceive回调中将其显示为0字节),然后关闭连接。

This is only a rough outline, but there are many, many things to work out. 这只是一个粗略的概述,但是还有很多很多事情要做。 Especially exception handling can be real tricky! 特别是异常处理可能非常棘手!

To make the entire thing more readable I would first split out the client and the server part into separate classes - otherwise all the send and read methods quickly get indistinguishable. 为了使整个内容更具可读性,我首先将客户端和服务器部分划分为单独的类-否则所有的send和read方法很快就会变得难以区分。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM