tags:

views:

2492

answers:

6

I'm developing a server application in C#. Clients can connect to the server and make various requests. Currently, when a client connects I spawn a new thread to handle the request(s). I'm using the TCPClient class to handle client connections. My server works as follows:

  1. Client connect to server with a request
  2. Server handles request
  3. Server waits to see if client has any more requests
  4. If client does not make another request within some timeout period, the server kills the connection

My problem is the following:

When I read from the NetworkStream object that I get from the TCPClient class, the NetworkStream Read() method does not block if there is no Data Available. When the server reaches step #3, I would like to set some timeout on the NetworkStream and if the client does not make any more requests within that duration the server should kill the connection when that timeout exception is thrown. When my server reaches step 3, the NetworkStream Read() method does not block, reguardless of what I set it's ReadTimeout property to be. Can anyone help me out, or suggest a better way to do what I'm trying to do.

Thanks

A: 

You should graduate from using Read() to the BeginRead() method and look into parallel processing. Network programming is not for the faint of heart.

Basically you don't want to create a loop polling for data.

while(!timedout && !stream.DataAvailable) sleep(0); // This is bad.
if(steam.DataAvailable) steam.Read();

Instead it makes more sense to do something like this. Create a reset event, call BeginRead(), and release the reset event on the callback. Something like the following:

void clientThread()
{
   stream.BeginRead(myCallback);
   resetEvent.WaitOne(timeout)
}
void myCallback
{
   resetEvent.Set();
}
Spencer Ruport
He already is spawning a new thread per client, in which case using BeginRead makes less sense....
Reed Copsey
It makes more sense than using sleep. http://msmvps.com/blogs/peterritchie/archive/2007/04/26/thread-sleep-is-a-sign-of-a-poorly-designed-program.aspx
Spencer Ruport
Spencer: I edited my answer to adjust using a waithandle, but in this case, I don't necessarily believe sleep is a bad thing - it really depends on whether or not the amount of time slept is critical (since Richie's main argument is that SleepEx provides no timeout or guarantee that you'll return in the time you request).
Reed Copsey
And it still makes less sense than my answer.
Spencer Ruport
A: 

You'll need to handle the "timeout" yourself, since NetworkStream.Read() doesn't provide a way to do that.

What you'll probably want to do in your client thread is use some combination of NetworkStream.DataAvailable and/or NetworkStream.CanRead in conjunction with a blocking/timeout mechanism (such as using a WaitHandle.WaitOne call with a timeout) to provide the client with time to make data available. If nothing comes in after a specified time, just shutdown the connection, and exit.

Reed Copsey
Ahem. http://msmvps.com/blogs/peterritchie/archive/2007/04/26/thread-sleep-is-a-sign-of-a-poorly-designed-program.aspx
Spencer Ruport
I don't always agree with Peter Richie's arguments in there, particularly in user constructed threads. He even (in the discussion) advocates using Thread.Sleep(1) in some cases. That being said, in this case, I think using a WaitHandle is probably a better design, so I changed my answer.
Reed Copsey
If the client thread is waiting for data but only using the Read() method it will need to be looping until data is available. If you don't use Sleep() with Read() that's even worse.
Spencer Ruport
Yes - I was suggesting a loop with a blocking mechanism of some form.
Reed Copsey
And what releases the block? Another thread polling the client stream for data? How is that a better idea than BeginRead()?
Spencer Ruport
WaitHandle.WaitOne allows a timeout to be specified. THis was actually Peter Richie's suggestion to many people to avoid sleep in operations like this...
Reed Copsey
That's no better if there's nothing to release the handle. See my answer.
Spencer Ruport
A: 

Wrap the NetworkStream in a BufferedStream. BufferedStream.Read will block until at least 1 byte is available.

Charlie
It seems that I can't set a timeout duration on a BufferedStream wrapped around a NetworkStream (System.InvalidOperationException: Timeouts are not supported on this stream...) So, I would still have to handle the timeout manually if I went this route
Setup timers to handle the timeouts. when the timer closes the socket an exception will be thrown on the read method. Then when data is read, reset the timer.
Charlie
+1  A: 

NetworkStream.Read and ReadByte should block until the timeout if there's no data to read. I think it's far more likely that the call is reading something, probably a bunch of zeroes. Check the data being sent by the client carefully.

Mirozell
I think just the same.
SoMoS
A: 

Is this what you're looking for?

http://msdn.microsoft.com/en-us/library/system.net.sockets.tcpclient.receivetimeout.aspx

Looks like you get the NetworkStream from the TcpClient using GetStream(), then call the Read() method on it.

yodaj007
A: 

I do believe that the day where you need to write your own protocol has gone, with the tools available to build complete protocol in seconds, recently, I used .Net protocol builder to build packets and client/server connections it works like a charm, the idea is to build your packets visually and the program will emit all the required code for these packets so you need to deal with objects rather than raw data (bytes) the program can be found in www.protocol-builder.com give it a try and see what I am talking about, ah by the way it generates documentation for your own protocol as well.

Nick