3

I'm currently writing a chat-server, bottom up, in C#.

It's like one single big room, with all the clients in, and then you can initiate private chats also. I've also laid the code out for future integration of multiple rooms (but not necessary right now).

It's been written mostly for fun, but also because I'm going to make a new chatsite for young people like myself, as there are no one left here in Denmark.

I've just tested it out with 170 clients (Written in Javascript with JQuery and a Flash bridge to socket connectivity). The response time on local network from a message being sent to it being delivered was less than 1 second. But now I'm considering what kind of performance I'm able to squeeze out of this.

I can see if I connect two clients and then 168 other, and write on client 2 and watch client 1, it comes up immediately on client 1. And the CPU usage and RAM usage shows no signs of server stress at all. It copes fine and I think it can scale to at least 1000 - 1500 without the slightest problem.

I have however noticed something, and that is if I open the 170 clients again and send a message on client 1 and watch on client 170, there is a log around 750 milliseconds or so. I know the problem, and that is, when the server receives a chat message it broadcasts it to every client on the server. It does however need to enumerate all these clients, and that takes time. The delay right now is very acceptable for a chat, but I'm worried client 1 sending to client 750 maybe (not tested yet) will take 2 - 3 seconds. And i'm also worried when I begin to get maybe 2 - 3 messages a second.

So to sum it up, I want to speed up the server broadcasting process. I'm already utilizing a parallel foreach loop and I'm also using asynchronous sockets.

Here is the broadcasting code:

  lock (_clientLock)
        {
            Parallel.ForEach(_clients, c =>
            {
                c.Value.Send(message);
            });
        }

And here is the send function being invoked on each client:

try {
        byte[] bytesOut = System.Text.Encoding.UTF8.GetBytes(message + "\0");
        _socket.BeginSend(bytesOut, 0, bytesOut.Length, SocketFlags.None, new AsyncCallback(OnSocketSent), null);
        }
        catch (Exception ex) { Drop(); }

I want to know if there is any way to speed this up? I've considered writing some kind of helper class accepting messages in a que and then using maybe 20 threads or so, to split up the broadcasting list.

But I want to know YOUR opinions on this topic, I'm a student and I want to learn! (:

Btw. I like how you spot problems in your code when about to post to stack overflow. I've now made an overloaded function to accept a byte array from the server class when using broadcast, so the UTF-8 conversion only needs to happen once. Also to play it safe, the calculation of the byte array length only happens once now. See the updated version below. But I'm still interested in ways of improving this even more!

Updated broadcast function:

 lock (_clientLock)
        {
            byte[] bytesOut = System.Text.Encoding.UTF8.GetBytes(message + "\0");
            int bytesOutLength = bytesOut.Length;
            Parallel.ForEach(_clients, c =>
            {
                c.Value.Send(bytesOut, bytesOutLength);
            });
        }

Updated send function on client object:

 public void Send(byte[] message, int length)
    {
        try
        {
            _socket.BeginSend(message, 0, length, SocketFlags.None, new AsyncCallback(OnSocketSent), null);
        }
        catch (Exception ex) { Drop(); }
    }
4

1 に答える 1

3

~1s sounds really slow for a local network. Average LAN latency is 0.3ms. Is Nagle enabled or disabled? I'm guessing it is enabled... so: change that (Socket.NoDelay). That does mean you have to take responsibility for not writing to the socket in an overly-fragmented way, of course - so don't drip the message in character-by-character. Assemble the message to send (or better: multiple outstanding messages) in memory, and send it as a unit.

于 2012-07-21T22:27:50.797 に答える