3

I wrote a TCP server to use the BeginAccept/EndAccept pattern. With this, I coded up a simple UnitTest using a TcpClient, and measured each portion. All tests are localhost, so I am surprised to see that TCP connection is consistently taking 1 second. I have set the Socket.NoDelay = true although I believe this only affects Send/Receive. I am not receiving the first packet of data. Any help or ideas on speed this up are appreciated.

Note: I can not change the client side to keep the connection open, and I need to be able to handle a lot of requests per second if possible.

Server Side:

public void Start()
{
    System.Net.IPHostEntry localhost = System.Net.Dns.GetHostEntry(System.Net.Dns.GetHostName());
    System.Net.IPEndPoint endpoint;
    int port = Properties.Settings.Default.ListenPort;

    //
    // Setup the connection to listen on the first IP, and specified port
    //
    endpoint = new IPEndPoint(IPAddress.Any, port);
    listenSocket = new Socket(endpoint.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
    listenSocket.Bind(endpoint);
    listenSocket.NoDelay = true; // do not wait 200 milliseconds for new data to be buffered on the connection
    listenSocket.Listen(int.MaxValue);
    Console.WriteLine("Listening on {0}:{1}", localhost.AddressList[0], port);

    //
    // Post the accept. The accept method will continuously post a new accept as soon as a connection is made
    //
    while (true)
    {
        accepted.Reset();
        Connection connection = connections.Pop();
        listenSocket.BeginAccept(AcceptCallback, connection);
        accepted.WaitOne();
    }
}

private static void AcceptCallback(IAsyncResult ar)
{
    accepted.Set();

    Connection connection = ar.AsyncState as Connection;
    Socket remoteSocket = null;

    try
    {
        remoteSocket = listenSocket.EndAccept(ar);
        remoteSocket.NoDelay = true;                
        connection.RemoteSocket = remoteSocket;

        //
        // Start the Receive cycle
        //
        Receive(connection);             
    }
    catch (SocketException)
    {
        Disconnect(connection);
    }           
}

Simple Test Client:

[TestMethod()]
public void ClientTest()
{
    TestContext.BeginTimer("Connection");
    TcpClient client = new TcpClient("localhost", 10300);
    NetworkStream stream = client.GetStream();
    TestContext.EndTimer("Connection");
    ...

Using a LoadTest I loaded 25 users, and the Transaction "Connection" always takes above 1 second.

esac
  • 24,099
  • 38
  • 122
  • 179
  • 1
    Try sniffing the TCP packets using Wireshark to see what is happening. – Sjoerd Apr 27 '12 at 06:55
  • 1
    @esac: may be, it is host name resolving issue. Yes, I see that host name is "localhost". But could you try to establish connection via IP address? – Dennis Apr 27 '12 at 07:09
  • Did you release the connections properly? Which operating system are you using as the server? If you are running a non-server version of Windows, chances are that you are slowed down by the connection limit. See this [thread](http://stackoverflow.com/questions/657874/max-tcp-connections-to-a-machine) for more information. – Jiaji Wu Apr 27 '12 at 10:39

2 Answers2

4

Not sure why, but simply changing this:

TestContext.BeginTimer("Connection");          
TcpClient client = new TcpClient("localhost", 10300);            
TestContext.EndTimer("Connection");

To this:

TestContext.BeginTimer("Connection");
TcpClient client = new TcpClient();
client.Connect("localhost", 10300);       
TestContext.EndTimer("Connection");

Drops the time from 1 second to .13 seconds. Will have to investigate as to why, but hopefully this will help somebody out in the future.

esac
  • 24,099
  • 38
  • 122
  • 179
  • Sorry for waking up an old issue, but I'm curious to if you ever got an explanation to this? Was this the actual fix or was there some other thing that was the cause of the delay? – jishi Jan 12 '15 at 14:18
  • @jishi See la-comadreja comment below - it explains it quite well. – esac Feb 12 '15 at 15:52
3

When you attempt to connect using the TcpClient constructor on a host that resolves to both Ipv6 and Ipv4 addresses, the connection using Ipv6 is attempted first. If it fails then it attempts to connect using the Ipv6 address. This is the cause of the 1 second delay. Here is the MSDN link:

La-comadreja
  • 5,627
  • 11
  • 36
  • 64
Wayne
  • 31
  • 1
  • 1
    You repeated Ipv6, so which really comes first? – Lance Roberts Jul 02 '14 at 13:36
  • This answered helped me. I think this circumstance was easier to understand from the server side: I bound my server to the IPv4 loopback, but then tried to connect via domain name "localhost" to the client, which resolves to both a IPv6 and IPv4 address. The client tried the IPv6 address first, but it failed because the server was not listening. It defaulted to the IPv4 address which worked, but slowly. I instead listened to the IPv6 loopback, and it worked fast again. – Paul K Dec 17 '15 at 18:21