System.IO.Stream.Read gets stuck
I have an HTTP connection with the server side using System.IO.Stream.Read to read the HTTP request body message. The problem is that once in a couple of minutes the server gets stuck on the Read statement and doesn't continue until the socket timeout has been reached or the client has closed the connection.
int bytesRead = 0;
while (bytesRead < contentLength)
{
int got = stream.Stream.Read(buffer.Buffer, bytesRead, contentLength - bytesRead);
bytesRead += got;
}
It could happen if the stream 开发者_运维问答did not have the amount of data specified by contentLength variable. This is not the case because when following the tcp stream with WireShark I see that the whole message body (as specified by contentLength) has reached the server machine.
It happens only in the first time that the while loop has been "used", i.e. only in the first time that the stream didn't have "contentLength" number of bytes to read in one try and the while loop had to be re-entered.
Why does it get stuck and does not continue reading data?
I wonder if the stream is reporting early termination; you should also look at whether Read
returned a non-positive number, i.e.
while (bytesRead < contentLength)
{
int got = stream.Stream.Read(
buffer.Buffer, bytesRead, contentLength - bytesRead);
if(got <= 0) throw new EndOfStreamException(string.Format(
"Expected {0} bytes; {1} bytes received", contentLength, bytesRead));
bytesRead += got;
}
Basically, if the stream has closed, every call to Read
will return non-positive (probably 0) - so your while
loop will become a tight cycle of "read 0, add 0, read 0, add 0, read 0, add 0, read 0, add 0".
As a final point, your approach suggests you are allocating a byte[]
based on the incoming content-length header; just a warning: make sure you sanity-check this and limit it to sane values, otherwise a DOS attack is trivial. Also, if possible I would suggest trying to use the streaming API where possible, to avoid having to load it all into memory at once (unless you have limited the incoming size such that this isn't a concern).
Try this implementation, your count (contentLength - bytesRead) is wrong. It should be the buffer size.
byte[] buffer = new byte[bufferSize];
int count;
while ((count = stream.Stream.Read(buffer, 0, buffer.Length)) != 0)
{
// do something with the buffer using count as the end marker
destination.Write(buffer, 0, count);
}
If you just want a byte array from the stream, which is more like what you are attempting:
byte[] buffer = stream.Stream.ToArray()
Or to copy to another buffer:
byte[] data = stream.Stream.ToArray();
Array.CopyTo(data , buffer.Buffer, data.Length)
I've had similar errors only with clients that didn't flush their streams. MSDN doc on System.IO.Stream.Read says: "The implementation will block until at least one byte of data can be read, in the event that no data is available.". So for some reason there is no data available. I think you could set a certain ReadTimeout
and stop waiting for more data after a reasonably short time.
A related question has also been posted here: C# NetworkStream.Read oddity. Maybe his solution can also help you.
It seems like it had something to do with the fact that I was using the Read method of both the NetworkStream (that's the call seen in the code above) and the StreamReader that was created by passing the NetworkStream object to it's constructor.
The NetworkStream.Read was used to read the http request message body while the StreamReader.Read was used to read the rest of the request (startline, headers...). Although the calls happened synchroniously by the same thread, it may have been the cause to the behavior I experienced.
When changing the code to work only with a Socket and perform the reading directly from the socket, it has fixed the problem.
精彩评论