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

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!
 
A

Andreas Huber

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,
 
I

Ignacio Machin ( .NET/ C# MVP )

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.
 
P

Peter Duniho


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
 
P

Peter Duniho

[...]
        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.
 
R

Rodolphe

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.
 
P

Peter Duniho

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
 
R

Rodolphe

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.
 
P

Peter Duniho

[...]
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.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top