views:

310

answers:

4

Hello everyone,

I read some C# chat source code & I see that: on chat server with a lot of connected clients, server listener will run in a separated thread & each connected client will also run in a separated thread. Code examples:

Start server & begin listening in a separated thread:

    public void StartListening()
        {

            // Get the IP of the first network device, however this can prove unreliable on certain configurations
            IPAddress ipaLocal = ipAddress;

            // Create the TCP listener object using the IP of the server and the specified port
            tlsClient = new TcpListener(1986);

            // Start the TCP listener and listen for connections
            tlsClient.Start();

            // The while loop will check for true in this before checking for connections
            ServRunning = true;

            // Start the new tread that hosts the listener
            thrListener = new Thread(KeepListening);
            thrListener.Start();
        }

private void KeepListening()
        {
            // While the server is running
            while (ServRunning == true)
            {
                // Accept a pending connection
                tcpClient = tlsClient.AcceptTcpClient();
                // Create a new instance of Connection
                Connection newConnection = new Connection(tcpClient);
            }
        }

And a connection will also run in a separated thread:

public Connection(TcpClient tcpCon)
        {
            tcpClient = tcpCon;
            // The thread that accepts the client and awaits messages
            thrSender = new Thread(AcceptClient);
            // The thread calls the AcceptClient() method
            thrSender.Start();
        }

So, if a chat server with 10000 connected clients, the chat server application will have 10002 threads (one main thread, one server thread & 10000 client threads). I think the chat server will be overhead with a big number of threads. Please help me a solution. Thanks.

UPDATE: I believe chat examples are only for learning networking & they are not suitable in real-world model. Please give me a real-world solution. Thanks.

A: 

To make the matter worse you would also have to communicate between some arbitrary number of threads (it's a chat server, people want to talk to each other, not themselves.) I would suggest looking into UDP - can be done with a single thread on the server and fits the network activity well - people rarely write more then couple of sentences at a time in chat exchanges, which is very convenient for size-limited UDP datagrams.

There are other approaches of course, but one sure thing though is that you will never be able to do thread per socket at that scale.

Nikolai N Fetissov
You mean that simple chat source codes are not suitable in real-world model.
Lu Lu
Yes, you will bump into limits on number of file descriptors, thread scheduling, blocking, etc. If you do need to go with TCP look into select/poll/epoll/kqueue with non-blocking sockets as others suggested.
Nikolai N Fetissov
+1  A: 

A standard mechanism to ease the burden is known as selection, which can multiplex multiple Socket instances to watch for the ones that are ready to be read or written to. See this document: http://codeidol.com/csharp/csharp-network/Csharp-Network-Programming-Classes/Csharp-Socket-Programming/ and scroll down to the section on select().

Chris Dennett
select() and non-blocking read/write are fundamentals of a high-perf, high-client count server.
kyoryu
A: 

1) You'll NEVER want that many threads running - even if you could get them to run on your box (which you can't - each thread has a stack associated with it that takes real RAM and as you start more and more and more you'll run out of physical resources in your box and watch it blow up).

2) You'll want to look into thread pooling - using a smaller amount of threads to tackle a larger amount of work - typically reading from a queue of work that you try to get through as quickly as possible.

3) You'll want to look into io completion ports - a means of having a callback when io (likek a disk read or a network io) is waiting for you to take action - think of a thread (or pool of threads) dedicated to getting io notifications and then shoving the action to take for that io into a queue and then another pool of threads that take care of the actual messaging/logging/etc.

4) What happens when you scale beyond one machine? Which you hope to do if you're successful right? :-) Typically people dedicate a set of N machines to chat - then they hash based on a identifier for the user (think a GUID that represented the user - or a UserID/bigint depending on what corresponds to some internal authentication token that is consistent from login to login) which allows them to deterministically route the user's status/state information to a specific machine in that set of N boxes dedicated to messaging. So if a user that hashes to server N[2] needs to check if theri friends ar logged in it is easy to know for each of their friends exactly which machine their friend's status should be in because the backend consistently hashes those friends to the IM machine that corresponds to each userid hash. (i.e. you know just from the userid what server in the farm should be handling the IM status for that user.

Just dont' think you're gonna spin up a bunch of threads and that will save the day. It's sloppy and works only in very small numbers.

Dave Quick
+1  A: 

If you use .Net framework 2.0 SP2 or higher, than you may use new asyncrhronous sockets model based on IO Completion ports. In this case you shouldn't create your own threads, because IO Completion ports do all job for you.

Here some examples:

tcpServer = new System.Net.Sockets.TcpListener(IPAddress.Any, port);
tcpServer.Start();
tcpServer.BeginAcceptSocket(EndAcceptSocket, tcpServer);


private void EndAcceptSocket(IAsyncResult asyncResult)
{
    TcpListener lister = (TcpListener)asyncResult.AsyncState;
    Socket sock = lister.EndAcceptSocket(asyncResult);
    //handle socket connection (you may add socket to you internal storage or something)

    //start accepting another sockets
    lister.BeginAcceptSocket(EndAcceptSocket, lister);


    SocketAsyncEventArgs e = new SocketAsyncEventArgs();
    e.Completed += ReceiveCompleted;
    e.SetBuffer(new byte[socketBufferSize], 0, socketBufferSize);
    sock.ReceiveAsync(e);
}


void ReceiveCompleted(object sender, SocketAsyncEventArgs e)
{
    var sock = (Socket)sender;
    if (!sock.Connected)
    {
      //handle socket disconnection
    }
    var buf = new byte[size];
    Array.Copy(e.Buffer, buf, size);
    //handle received data

    //start reading new data
    sock.ReceiveAsync(e);
}
Sergey Teplyakov