jonah_muller jonah_muller - 1 month ago 53
C# Question

Async socket server with multiple clients connected

I want to write a asynchronous socket server and client in 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. 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?

Answer

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!).

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. I.e. in the AcceptCallback, you should save the handler in a list. And to keep the connections open, remove the handler.Shutdown() and handler.Close() in SendCallback. 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

  3. When a client connects, the listening sockets calls back:

    3a. Calling EndAccept on the listener socket provides another socket where the actual communication takes place.

    3b. To begin communication, call BeginReceive on the communication socket, and provide a callback where messages from the client will be received

    3c. Call BeginAccept again to indicate that you are ready to receive new connection

  4. When a message is received, the communication socket calls back:

    4a. Call EndReceive to get bytes read. If byte count is 0, the other side is disconnecting

    4b. Process the message and call BeginReceive to receive next message

  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.

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.