I'm writing a TCP server, and at the very heart of it is a fairly standard bind-listen-accept piece of code nicely encapsulated by TcpListener
. The code I'm running in development now works, but I'm looking for some discussion of the thread model I chose:
// Set up the socket listener
// *THIS* is running on a System.Threading.Thread, of course.
tpcListener = new TcpListener(IPAddress.Any, myPort);
tpcListener.Start();
while (true)
{
Socket so = tpcListener.AcceptSocket();
try
{
MyWorkUnit work = new MyWorkUnit(so);
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(DispatchWork);
bw.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(SendReply);
bw.RunWorkerAsync(work);
}
catch (System.Exception ex)
{
EventLogging.WindowsLog("Error caught: " +
ex.Message, EventLogging.EventType.Error);
}
}
I've seen good descriptions of which kind of thread model to pick (BackgroundWorker, stock Thread, or ThreadPool) but none of them for this kind of situation. A nice summary of the pros and cons of each is backgroundworker-vs-background-thread (second answer). In the sample code above, I picked BackgroundWorker because it was easy. It's time to figure out if this is the right way to do it.
These are the particulars of this application, and they're probably pretty standard for most transaction-like TCP servers:
- Not a Windows Forms app. In fact, it's run as Windows Service.
- (I'm not sure whether the spawned work needs to be a foreground thread or not. I'm running them as background now and things are okay.)
- Whatever priority assigned to the thread is fine as long as the
Accept()
loop gets cycles. - I don't need a fixed ID for the threads for later
Abort()
or whatever. - Tasks run in the threads are short -- seconds at most.
- Potentially lots of tasks could hit this loop very quickly.
- A "graceful" way of refusing (or queuing) new work would be nice if I'm out of threads.
So which is the right way to go on this?