Issue
I have separate client and server apps written in C#. When I test the server by connecting to it with PuTTY, when I close PuTTY the server detects end of stream and exits nicely at the return statement in the function below. But when I use my own client application, the NetworkStream on the server side always throws a System.IO.IOException "Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.", with an InnerException of type System.Net.Sockets.SocketException "An existing connection was forcibly closed by the remote host.".
The server code looks like this (LineReader and ProcessCommand are my own code).
public static async Task ServeStream(Stream stream, CancellationToken token)
{
LineReader reader = new LineReader(stream, Encoding.ASCII, 1024);
while (!token.IsCancellationRequested && !reader.EndOfStream)
{
string command = await reader.ReadLineAsync(token);
if (reader.EndOfStream)
return;
byte[] response = await ProcessCommand(command);
stream.Write(response, 0, response.Length);
}
}
The stream passed in is a NetworkStream obtained from TcpClient.GetStream(). The TcpClient in turn was obtained from TcpListener.AcceptTcpClientAsync().
And here is ReadLineAsync():
public async Task<string> ReadLineAsync(CancellationToken token)
{
if (lines.Count > 0)
return lines.Dequeue();
for (; ; )
{
int n = await Stream.ReadAsync(buffer, 0, buffer.Length, token);
if (n <= 0)
{
EndOfStream = true;
return string.Empty;
}
// ... lots of buffer management ...
}
}
On the server side I am also using a TcpClient and a NetworkStream. I have tried calling TcpClient.Close(), TcpClient.Client.Close(), and NetworkStream.Close() (TcpClient.Client is the underlying socket). In all three cases I get the above exception from Stream.ReadAsync() inside reader.ReadLineAsync(), rather than the clean end of stream I get when disconnecting PuTTY from my server. Is there any way to fix this?
Solution
Thank you all for your responses. Sorry for the delay in replying, I was taken ill, and when I got back to work, well, there was a lot of work.
TL;DR: the problem was caused by closing the stream with data still in it.
Initially, changing from calling tcpClient.Close() to calling socket.Shutdown(Both) did NOT fix the problem. In the process of writing a minimal example to post here without my LineReader code, I determined that the problem was that I had scaled up my test to do some performance testing and hadn't increased the buffer accordingly. So the client didn't read all the bytes that come back from the server. When I closed the stream on the client side without having read everything, it caused the exception on the server side. Simply increasing the read buffer size fixed this. After fixing this I tried going back to Stream.Close() and that also worked correctly. So calling Close instead of Shutdown wasn't really the problem in the first place. @Stephen Cleary's comments still seem relevant however, as in his linked video he is always reading, so calling Close with a read pending causes the problem, in my case there was also some slightly different unfinished business.
Also, as @Adam Cohen pointed out, I would have been better off using StreamReader. I had tried that initially but had some problems with it that I now can't reproduce.
Answered By - NⵙⵙB Answer Checked By - Marilyn (PHPFixing Volunteer)
0 Comments:
Post a Comment
Note: Only a member of this blog may post a comment.