[C# .NET 2.0] Weird behaviour with Close method of TcpClient class

  • Thread starter Thread starter Robert Lochon
  • Start date Start date
R

Robert Lochon

Hello,

I send some data to a server (but don't wait for any answer) with this
piece of code:

private void Send()
{
TcpClient client = null;

try
{
client = new TcpClient(s_host, s_port);
}
catch
{
PushLog("Failed to connect to {0}:{1}...", s_host, s_port);
return;
}

NetworkStream networkStream = client.GetStream();
StreamWriter streamWriter = new StreamWriter(networkStream);
streamWriter.AutoFlush = true;
streamWriter.NewLine = "\r\n";

try
{
string req = string.Format("foo", s_loginName);
streamWriter.WriteLine("POST /myendpoint?message=foo HTTP/1.0\r
\nContent-Type: text/plain; charset=utf-8\r\nContent-Length: 0\r\n");
}
catch (Exception err)
{
PushLog("Failed to send message...\n-> {0}", err.Message);
}
finally
{
networkStream.Dispose();
client.Close();
}
}

The problem I have with this code is that the server receives my
message approximatively 1 time out of 10.
But if I remove the finally block (NetworkStream and TcpClient
closing), the server receives my message every time.

What am I doing wrong here?
I'm pretty sure I have to close my connection and I read in some doc
that the underlying NetworkStream should also be closed.

Why is it working sometimes?
Should I specify a SendTimeout value?

Thanks for your help!
 
The problem I have with this code is that the server receives my
message approximatively 1 time out of 10.
But if I remove the finally block (NetworkStream and TcpClient
closing), the server receives my message every time.

What am I doing wrong here?
I'm pretty sure I have to close my connection and I read in some doc
that the underlying NetworkStream should also be closed.

You might try the LingerOption property:

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

HTH,
 
Hello,

I send some data to a server (but don't wait for any answer) with this
piece of code:

private void Send()
{
    TcpClient client = null;

    try
    {
        client = new TcpClient(s_host, s_port);
    }
    catch
    {
        PushLog("Failed to connect to {0}:{1}...", s_host, s_port);
        return;
    }

    NetworkStream networkStream = client.GetStream();
    StreamWriter streamWriter = new StreamWriter(networkStream);
    streamWriter.AutoFlush = true;
    streamWriter.NewLine = "\r\n";

    try
    {
        string req = string.Format("foo", s_loginName);
        streamWriter.WriteLine("POST /myendpoint?message=foo HTTP/1.0\r
\nContent-Type: text/plain; charset=utf-8\r\nContent-Length: 0\r\n");
    }
    catch (Exception err)
    {
        PushLog("Failed to send message...\n-> {0}", err.Message);
    }
    finally
    {
        networkStream.Dispose();
        client.Close();
    }

}

The problem I have with this code is that the server receives my
message approximatively 1 time out of 10.
But if I remove the finally block (NetworkStream and TcpClient
closing), the server receives my message every time.

What am I doing wrong here?
I'm pretty sure I have to close my connection and I read in some doc
that the underlying NetworkStream should also be closed.

Why is it working sometimes?
Should I specify a SendTimeout value?

Thanks for your help!

Hi,

I think your problem is that you selected the wrong protocol, if you
want to send a piece of data and simply forget about it you should use
UDP.
 

Unfortunately, at least according to the documentation, you can only
_shorten_ the linger time for the socket using that property:

When the LingerTime property stored in the LingerState property
is set greater than the default IP protocol time-out, the default
IP protocol time-out will still apply and override.

I believe that the more general problem is that the original code is not
calling Socket.Shutdown() before disposing/closing the stream and socket.
I'm not an expert, but my recollection is that closing an open socket
results in a "reset" of the connection (i.e. "RST" packet being sent)
while a shutdown results in a "graceful closure" of the connection (i.e.
"FIN" packet being sent). Some quick Googling seems to confirm this, with
the additional wrinkle that the close initially causes a "FIN", but the
network stack will send a "RST" if the other end tries to send any data.

So, it would be worthwhile to try a true graceful closure. Call
Socket.Shutdown() after all the data is sent and then go ahead and wait
for the full graceful closure, by reading from the socket until the
end-of-stream is indicated (by a 0 byte read). As I understand it, that
will ensure that both ends get a chance to read the data until they've
both agree to close their sockets.

Pete
 
[...]
        streamWriter.WriteLine("POST /myendpoint?message=foo HTTP/1.0\r
\nContent-Type: text/plain; charset=utf-8\r\nContent-Length: 0\r\n");
[...]

Hi,

I think your problem is that you selected the wrong protocol, if you
want to send a piece of data and simply forget about it you should use
UDP.

He's (apparently) sending to an HTTP server. He doesn't have the choice
of what protocol to use.
 
Hi everybody,

Thank you all for your help.
I finally solved my problem by adding this piece of code just after
"streamWriter.WriteLine(...)":

StreamReader streamReader = new StreamReader
(networkStream);
streamReader.ReadToEnd();

Before that, I tried to use the LingerOption property but to no avail.

The solution is pretty simple but I'm not sure I fully understand why
I should read something that I don't care about.
 
Hi everybody,

Thank you all for your help.
I finally solved my problem by adding this piece of code just after
"streamWriter.WriteLine(...)":

StreamReader streamReader = new StreamReader
(networkStream);
streamReader.ReadToEnd();

Before that, I tried to use the LingerOption property but to no avail.

The solution is pretty simple but I'm not sure I fully understand why
I should read something that I don't care about.

Because, lacking a proper graceful closure of the socket (which requires
calling Socket.Shutdown()), the network stack assumes that when you call
Close(), it's a hard reset and translates that accordingly into the
transmission of an RST, which includes discarding any untransmitted data.

By reading to the end of the stream (albeit in not a very efficient way),
you force the Close() to be delayed until you know that the other end has
closed the connection. Which, presumably, it doesn't do until it's read
all the data you sent.

It still would be better for you to call Socket.Shutdown(), and as far as
reading until the end-of-stream, you can accomplish that more efficiently
simply by using the Socket.Read() method until it returns 0, rather than
bothering with a StreamReader (you aren't going to ever look at the read
data, so why bother going to all the trouble to translate bytes to
characters?).

Pete
 
Because, lacking a proper graceful closure of the socket (which requires
calling Socket.Shutdown()), the network stack assumes that when you call
Close(), it's a hard reset and translates that accordingly into the
transmission of an RST, which includes discarding any untransmitted data.

By reading to the end of the stream (albeit in not a very efficient way),
you force the Close() to be delayed until you know that the other end has
closed the connection. Which, presumably, it doesn't do until it's read
all the data you sent.

OK. Thanks for the explanation.


It still would be better for you to call Socket.Shutdown(), and as far as
reading until the end-of-stream, you can accomplish that more efficiently
simply by using the Socket.Read() method until it returns 0, rather than
bothering with a StreamReader (you aren't going to ever look at the read
data, so why bother going to all the trouble to translate bytes to
characters?).

Well, I tried to replace:
StreamReader streamReader = new StreamReader
(networkStream);
streamReader.ReadToEnd();

with:
tcpClient.Client.Shutdown(SocketShutdown.Send);
byte[] buffer = new byte[256];
while (tcpClient.Client.Receive(buffer) > 0) ;

But this way, not all my requests reach the server...
Whereas with the StreamReader solution, everything works fine.
 
[...]
Well, I tried to replace:
StreamReader streamReader = new
StreamReader(networkStream);
streamReader.ReadToEnd();

with:
tcpClient.Client.Shutdown(SocketShutdown.Send);
byte[] buffer = new byte[256];
while (tcpClient.Client.Receive(buffer) > 0) ;

But this way, not all my requests reach the server...
Whereas with the StreamReader solution, everything works fine.

That doesn't make sense. StreamReader.ReadToEnd() does basically the same
thing your explicit code does. Since you've already got a TcpClient, I
would've just used the NetworkStream directly to read, since you've
already got that. But either way should work the same. Maybe there's
something about mixing Socket and NetworkStream i/o that is messing things
up.
 
Back
Top