Matt Johnson-Pint
Matt Johnson-Pint

Reputation: 241603

Why is the TcpClient slower to connect with parameters in the constructor?

I have a problem with System.Net.Sockets.TcpClient.

A simple test app just opens a connection, sends some data, and closes. There's a simple server on the other end, whose performance is just fine.

The code looked something like this:

var client = new TcpClient("localhost", 1234);
using (var stream = client.GetStream())
using (var writer = new StreamWriter(stream))
{
    writer.Write("foo");
    writer.flush();
}
client.Close();

It works just fine, but I noticed that the unit test was taking > 1000ms to run. When I put it in a loop called 10 times, it was > 10,000ms.

After hours of debugging with timings on both the client and the server, I found where it was slow.
The fix was to change the code from this:

var client = new TcpClient("localhost", 1234);

to this:

var client = new TcpClient();
client.Connect("localhost", 1234);

That made all the difference. One pass takes about 10ms now, and 10 passes is a little less than 100ms.

WHY???

Upvotes: 6

Views: 3704

Answers (2)

Christopher Swiedler
Christopher Swiedler

Reputation: 86

From http://msdn.microsoft.com/en-us/library/115ytk56(v=vs.110).aspx:

"If IPv6 is enabled and the TcpClient(String, Int32) method is called to connect to a host that resolves to both IPv6 and IPv4 addresses, the connection to the IPv6 address will be attempted first before the IPv4 address. This may have the effect of delaying the time to establish the connection if the host is not listening on the IPv6 address."

I'm not sure why the default constructor doesn't do this too (I would have expected you to have to use the constructor that takes an AddressFamily and specify IPv4 before connecting) but apparently it doesn't.

Upvotes: 7

Gary Walker
Gary Walker

Reputation: 9134

You will love this answer. I Don't know, because does not make sense to me.

Using Reflector, the default ctor in the v.2 assembly is

public TcpClient() : this(AddressFamily.InterNetwork)
{
    if (Logging.On)
    {
        Logging.Enter(Logging.Sockets, this, "TcpClient", (string) null);
    }
    if (Logging.On)
    {
        Logging.Exit(Logging.Sockets, this, "TcpClient", (string) null);
    }
}

The code above is very nearly a no-op if you don't have logging enabled

The 2nd ctor(string hostname, int port) does all of this and more of course, but part of that "more" is the call to the same TcpClient.Connect() method you use in you second example. i.e., the executed code is very nearly identical in both cases. I dug in because I wondered how the MS code could have such a weird problem -- as best I can tell by looking at the dissambly, they don't have a weird problem. I thought I might see some odd DNS lookup problem, or suchlike but no dice.

Connecting the socket is almost guaranteed to be to slow part, but I would expect this to take up to about 100 or 200 ms at most unless you have a really bad Internet Connection, or DNS server, or Proxy Server. But I see nothing that would cause a significantly different behavior in your 2 cases with 1 exception. Your examples are not complete in showing your how your using clause might differ. If there is cleanup code on the socket that is triggering slow behavior, there could be a difference in your 2 examples.

Upvotes: -1

Related Questions