views:

307

answers:

4

I am using the TcpClient class in C#.

Each time there is a new tcp connection request, the usual practice is to create a new thread to handle it. And it should be possible for the main thread to terminate these handler threads anytime.

My solution for each of these handler thread is as follows:

1 Check NetworkStream's DataAvailable method
    1.1 If new data available then read and process new data
    1.2 If end of stream then self terminate
2 Check for terminate signal from main thread
    2.1 If terminate signal activated then self terminate
3 Goto 1.

The problem with this polling approach is that all of these handler threads will be taking up significant processor resources and especially so if there is a huge number of these threads. This makes it highly inefficient.

Is there a better way of doing this?

A: 

I remember working on a similar kind of Windows Service. It was a NTRIP Server that can take around 1000 TCP connections and route the data to a NTRIP Caster.

If you have a dedicated server for this application then it will not be a problem unless you add more code to each thread (File IO, Database etc - although in my case I also had Database processing to log the in/out for each connection).

The things to watch out for:

  1. Bandwidth when the threads goes up to 600 or so. You will start seeing disconnections when the TCP Buffer window is choked for some reason or the available bandwidth falls short
  2. The operating system on which you are running this application might have some restrictions, which can cause disconnections

The above might not be applicable in your case but I just wanted it put it here because I faced then during development.

A9S6
+2  A: 

See Asynchronous Server Socket Example to learn how to do this the ".NET way", without creating new threads for each request.

John Saunders
However, the asynchronous I/O way does not provide for a way to provide for termination. Say for some reason, the user wants to terminate the application and there are some asynchronous call which has yet to call the respective callback function because no incoming data has occured yet but there must be an immediate termination of the application without delay.
Lopper
Well, Async IO will use the threads (pool) internally instead of creating them. So in my opinion if you have large number of long-lived connections its better to have a dedicated thread for each of them instead of using the thread pool as other applications that use the ThreadPool might get affected by this.
A9S6
@Lopper: one way to terminate is to simply not start the next operation. Done reading? Don't do another BeginRead. I also suspect that closing the `TcpClient` instance will do a cancel.
John Saunders
@A9S6: Async IO will only use a thread pool thread when there's something to do. Otherwise, no thread is used at all. If the processing performed during the callback takes too long, that would be a reason to not do it on the ThreadPool thread. Remember that Async IO never blocks any thread - not even a ThreadPool thread.
John Saunders
A: 

You're right that you do not want all of your threads "busy waiting" (i.e. running a small loop over and over). You either want them blocking, or you want to use asynchronous I/O.

As John Saunders mentioned, asynchronous I/O is the "right way" to do this, since it can scale up to hundreds of connections. Basically, you call BeginRead() and pass it a callback function. BeginRead() returns immediately, and when data arrives, the callback function is invoked on a thread from the thread pool. The callback function processes the data, calls BeginRead() again, and then returns, which releases the thread back into the pool.

However, if you'll only be holding a handful of connections open at a time, it's perfectly fine to create a thread for each connection. Instead of checking the DataAvailable property in a loop, go ahead and call Read(). The thread will block, consuming no CPU, until data is available to read. If the connection is lost, or you close it from another thread, the Read() call will throw an exception, which you can handle by terminating your reader thread.

Aaron
However, the asynchronous I/O way does not provide for a way to provide for termination. Say for some reason, the user wants to terminate the application and there are some asynchronous call which has yet to call the respective callback function because no incoming data has occured yet.
Lopper
+1  A: 

Believe it or not that 1000 tick sleep will really keep things running smooth.

private readonly Queue<Socket> sockets = new Queue<Socket>();
private readonly object locker = new object();
private readonly TimeSpan sleepTimeSpan = new TimeSpan(1000);
private volatile Boolean terminate;

private void HandleRequests() 
{
    Socket socket = null;

    while (!terminate)
    {
        lock (locker)
        {
            socket = null;
            if (sockets.Count > 0)
            {
                socket = sockets.Dequeue();
            }
        }

        if (socket != null)
        {
            // process
        }

        Thread.Sleep(sleepTimeSpan);
    }   
}
ChaosPandion
Correct me if I am wrong but what the above code meant that only one thread will be performing the reading/processing of incoming data from tcp connection. If significant time is needed for the processing of the data inside the if(socket!=null) block, then I suppose there would be a significant delay before processing the other connection. Is there a better way where processing of each socket can proceed in parallel but at the same time not use the polling method?
Lopper
No no, this is the method you pass to your handler threads when they are created.
ChaosPandion