D
Dave A
I have an application that does lots of socket communications all asynchronously via the TcpClient class.
The code has been working 99.9999% of the time (yeah one of those bugs) but occasionally the receiving thread would get 'stuck'.
The pattern that I have used from the reading is quite common...
AutoResetEvent waitForReadToComplete;
NetworkStream stream;
.... code to start the socket, send some data and then read the data by calling...
private void ReceiveDataOfTheSocket()
{
try
{
// Convert the request into a byte array
waitForReadToComplete = new AutoResetEvent(false);
IAsyncResult ar = stream.BeginRead(receiveBytes, 0, tcpClient.ReceiveBufferSize, new AsyncCallback(ReadAsync), stream);
// Wait for the receive to complete...
waitForReadToComplete.WaitOne();
...
}
catch(Exception exp)
{
.. does something meaningful
}
}
private void ReadAsync(IAsyncResult ar)
{
try
{
... read data off the socket
if (amountOfDataReadOfTheSocket > 0)
{
// Recall this routine
streamAsync.BeginRead(receiveBytes, 0, tcpClient.ReceiveBufferSize, new AsyncCallback(ReadAsync), streamAsync);
}
else
{
waitForReadToComplete.Set();
}
}
catch(Exception exp)
{
.. do some stuff and then..
waitForReadToComplete.Set();
}
}
After maybe a day or two of running successfully the application would appear to be stopped. In the debugger I was able to see that one thread was stopped at waitForReadToComplete.WaitOne(); in the ReceiveData() function yet there was no other thread running in ReadAsync(). There were other threads in the application but they were not affecting this code and were working as expected. Thread starvation was not an issue.
It was as if .BeginRead() had been called successfully but either ReadAsync() had either not been run or it had run and somehow waitForReadToComplete.Set(); had not been called despite the exception handler.
I took a bit of a guess and thought that BeginRead() had somehow failed yet no exception had been thrown.
I changed the line...
waitForReadToComplete.WaitOne();
to
while ((waitForReadToComplete.WaitOne(5000, true) == false) && (ar.IsCompleted == false))
{
// Do nothing
}
....which effectively waits for the asyncread to complete or the ar.IsCompleted to be set to true - whatever comes first.
The program has now been running for days without a problem.
I am not one to say "lets not worry about it since it is working". I want to know why this as.IsCompleted is required when waitforReadToComplete.WaitOne() should be more than sufficient.
This program is following the HTTP protocol. The only thing that I can think of is that the HTTP send has worked and then the .BeginRead() is called. At this moment the socket is connected so no exception is thrown. However, it is then closed in the moments thereafter and ReadAsync() is therefore never called. The only indication is that ar.IsCompleted is set to true.
Another idea that I have is that the MSDN help suggests that the code in ReadAsync() that re-sets up the async read should be...
// message received may be larger than buffer size so loop through until you have it all.
while(myNetworkStream.DataAvailable){
myNetworkStream.BeginRead(myReadBuffer, 0, myReadBuffer.Length,
new AsyncCallback(ReadAsync),
myNetworkStream);
}
Maybe if I did that then it would work better. ...but I can't see this as being very good code. It is saying that in the place where data is received off the socket, repeatedly asynchronously re-read off the socket and make a callback to this routhine until you have read all of the data. Surely the "while(myNetworkStream.DataAvailable){" is totally redundant and possibly quite foolish?
Is there a better way to code this? Does anyone have any better ideas about what maybe causing it or how to check for it? So many questions.
Thanks in advance.
Dave A
The code has been working 99.9999% of the time (yeah one of those bugs) but occasionally the receiving thread would get 'stuck'.
The pattern that I have used from the reading is quite common...
AutoResetEvent waitForReadToComplete;
NetworkStream stream;
.... code to start the socket, send some data and then read the data by calling...
private void ReceiveDataOfTheSocket()
{
try
{
// Convert the request into a byte array
waitForReadToComplete = new AutoResetEvent(false);
IAsyncResult ar = stream.BeginRead(receiveBytes, 0, tcpClient.ReceiveBufferSize, new AsyncCallback(ReadAsync), stream);
// Wait for the receive to complete...
waitForReadToComplete.WaitOne();
...
}
catch(Exception exp)
{
.. does something meaningful
}
}
private void ReadAsync(IAsyncResult ar)
{
try
{
... read data off the socket
if (amountOfDataReadOfTheSocket > 0)
{
// Recall this routine
streamAsync.BeginRead(receiveBytes, 0, tcpClient.ReceiveBufferSize, new AsyncCallback(ReadAsync), streamAsync);
}
else
{
waitForReadToComplete.Set();
}
}
catch(Exception exp)
{
.. do some stuff and then..
waitForReadToComplete.Set();
}
}
After maybe a day or two of running successfully the application would appear to be stopped. In the debugger I was able to see that one thread was stopped at waitForReadToComplete.WaitOne(); in the ReceiveData() function yet there was no other thread running in ReadAsync(). There were other threads in the application but they were not affecting this code and were working as expected. Thread starvation was not an issue.
It was as if .BeginRead() had been called successfully but either ReadAsync() had either not been run or it had run and somehow waitForReadToComplete.Set(); had not been called despite the exception handler.
I took a bit of a guess and thought that BeginRead() had somehow failed yet no exception had been thrown.
I changed the line...
waitForReadToComplete.WaitOne();
to
while ((waitForReadToComplete.WaitOne(5000, true) == false) && (ar.IsCompleted == false))
{
// Do nothing
}
....which effectively waits for the asyncread to complete or the ar.IsCompleted to be set to true - whatever comes first.
The program has now been running for days without a problem.
I am not one to say "lets not worry about it since it is working". I want to know why this as.IsCompleted is required when waitforReadToComplete.WaitOne() should be more than sufficient.
This program is following the HTTP protocol. The only thing that I can think of is that the HTTP send has worked and then the .BeginRead() is called. At this moment the socket is connected so no exception is thrown. However, it is then closed in the moments thereafter and ReadAsync() is therefore never called. The only indication is that ar.IsCompleted is set to true.
Another idea that I have is that the MSDN help suggests that the code in ReadAsync() that re-sets up the async read should be...
// message received may be larger than buffer size so loop through until you have it all.
while(myNetworkStream.DataAvailable){
myNetworkStream.BeginRead(myReadBuffer, 0, myReadBuffer.Length,
new AsyncCallback(ReadAsync),
myNetworkStream);
}
Maybe if I did that then it would work better. ...but I can't see this as being very good code. It is saying that in the place where data is received off the socket, repeatedly asynchronously re-read off the socket and make a callback to this routhine until you have read all of the data. Surely the "while(myNetworkStream.DataAvailable){" is totally redundant and possibly quite foolish?
Is there a better way to code this? Does anyone have any better ideas about what maybe causing it or how to check for it? So many questions.
Thanks in advance.
Dave A