在1分钟的空闲间隔后,TCP首次连接建立时间增加到1-3秒

时间:2018-03-20 15:03:06

标签: c# tcp timeout windows-10

我的原始场景(see the question here)是我有一百个小文本文件,我想加载,解析和存储在DLL中。 DLL的客户端是瞬态的(命令行程序),我宁愿不在每个命令行调用上重新加载数据。 (但这篇文章是关于我在下面描述的实验中的TCP客户端/服务器IPv4 / IPv6连接的矩阵。)

所以,我想我会编写一个Windows服务器来存储数据,并让客户端使用TCP查询服务器。但是,TCP性能确实很慢。我使用Stopwatch编写了以下代码来测量套接字设置时间。

    // time the TCP interaction to see where the time goes
    var stopwatch = new Stopwatch();
    stopwatch.Start();

    // create and connect socket to remote host
    client = new TcpClient (hostname, hostport); // auto-connects to server
    Console.WriteLine ("Connected to {0}",hostname);

    // get a stream handle from the connected client
    netstream = client.GetStream();

    // send the command to the far end
    netstream.Write(sendbuf, 0, sendbuf.Length);
    Console.WriteLine ("Sent command to far end: '{0}'",cmd);
    stopwatch.Stop();
    sendTime = stopwatch.ElapsedMilliseconds;

令我惊讶的是,这一小段代码需要花费1,037毫秒(1秒)来执行。我预计时间要小得多。设置时间为1秒的原因是new TcpClient(hostname, port)代码的语法导致DNS返回IPv6和IPv4地址。在连接期间,客户端首先尝试IPv6到服务器,并且必须等待超时才能回退到IPv4(服务器正在使用)。

经过一次伟大的answer from @Evk about TcpClient syntax and IPv6 / IPv4 here之后,我做了以下几个实验。三个客户端和两个服务器用于测试各种语法和行为:

Client 1: DNS returns only IPv4 using new TcpClient().
Client 2: DNS returns only Ipv6 using new TcpClient(AddressFamily.InternetworkV6)
Client 3: DNS returns IPv4 and IPv6 using new TcpClient(“localhost”,port)
Server 1: IPv4 new TcpListener(IPAddress.Loopback, port)
Server 2: IPv6 new TcpListener(IPAddress.IPv6Loopback, port)

从最差到最好,6对可能的对返回以下结果:

c4xs6 - 客户端1 ip4与服务器2 ip6 - 连接被主动拒绝。

c6xs4 - 客户端2 ip6与服务器1 ip4 - 连接被主动拒绝。

c46xs4 - 客户端3(同时使用v6和v4)与服务器1 ip4,始终延迟1000毫秒,因为客户端在超时和尝试ip4之前尝试使用IPv6,这是一致的。这是本文中的原始代码。

C46xs6 - 客户端3(同时使用v6和v4)与服务器2 ip6,在两次重新启动后,在第一次尝试(21ms)和随后的紧密间隔尝试时速度很快。但是等了一两分钟后,接下来的尝试是3000毫秒,然后在紧密间隔的后续尝试中快速20毫秒。

C4xs4 - 与上述行为相同。首次尝试重新启动后很快,后续尝试间隔很短。但等了一两分钟后,接下来的尝试是3000毫秒,接着是快速(20毫秒)紧密间隔的后续尝试。

C6xS6 - 与上述行为相同。新服务器重新启动后快速,但一两分钟后,观察到延迟尝试(3000毫秒),然后快速(20毫秒)响应紧密间隔的尝试。

我的实验表明随着时间的推移没有始终如一的快速反当连接空闲时,必须存在某种延迟或超时或休眠行为。我使用netstream.Close; client.Close();在每次尝试时关闭每个连接。 (是吗?)我不知道在一两分钟的闲置无活动连接时间之后可能导致延迟响应的原因。

知道在一两分钟的空闲收听时间之后可能导致延迟的原因是什么?据说客户端退出系统内存,退出了控制台程序。据说服务器没什么新意,只是在听另一个连接。

重新启动服务器可以提供快速的初始(并且间隔很近(10秒)响应,但是在1或2分钟延迟后的第一次连接会产生缓慢的响应。当事情闲置一段时间后,它就像是服务器决定一些事情并开始让客户端在响应之前等待一点。我建议服务器出错,因为(1)客户端在空闲时间内甚至没有运行,因此无法做出任何决定;(2) )重新启动服务器会给出快速响应。

保持活动ping似乎不合适,因为客户端不会尝试保持连接打开 - 它会在每次短暂呼叫后关闭网络流和客户端,如上所示。

有没有人知道发生了什么,以及我能做些什么(如果有的话)?

编辑#1 - 几乎完整的源代码,如下所述

客户代码:

try {
    var stopwatch = new Stopwatch();
    stopwatch.Start();

    client = new TcpClient(); // DNS forces use of v4
    client.Connect("localhost", hostport);
    netstream = client.GetStream();
    netstream.Write(sendbuf, 0, sendbuf.Length);

    stopwatch.Stop();
    sendTime = stopwatch.ElapsedMilliseconds;
    Console.WriteLine($"Milliseconds for sending by TCP:  '{sendTime}'");
     // receive the bytes back from the far end
    ...
catch (Exception ex) {
    Console.WriteLine (ex.Message);
}
finally {
    client?.Close(); // close the client after receiving server response
}

服务器代码:

listener = null;

// try to start a listener
listener = new TcpListener(IPAddress.Loopback, hostport); // uses v4
Console.WriteLine("Listening on v4 only...");
listener.Start();
Console.WriteLine("Listener is listening on port {0}", hostport);
rxbuf = new byte[BUFSIZE];              // the buffer
inbytes = 0;                            // nbytes rx
// loop forever handling clients one at a time
for (; ; ) {
    client = null;
    netstream = null;

    try {
        client = listener.AcceptTcpClient(); // get client connection
        netstream = client.GetStream(); // get the receiving stream

        Console.WriteLine("Accepted a client...Reading...");
        totalbytes = 0;

        inbytes = netstream.Read(rxbuf, // the buf
        totalbytes,                     // write new bytes here
        rxbuf.Length - totalbytes);     // read this many bytes
        if (inbytes == 0)               // if no bytes read
            break;                      // break loop

        totalbytes = totalbytes + inbytes; // update total bytes read
        inbytes = 0;
         // - show what you received from the client
        backcmd = Encoding.ASCII.GetString(rxbuf, 0, totalbytes);
        Console.WriteLine("Rx command: '{0}'", backcmd);
        Console.WriteLine("Running command: '{0}'", backcmd);
         // - get a new process info structure to run the external command
        System.Diagnostics.Process proc = new System.Diagnostics.Process();
        proc.StartInfo.UseShellExecute = false;
        ... run the 40-char cmd from the client
        ... collect the output from the console app run on the server side
        string output = proc.StandardOutput.ReadToEnd();
        proc.WaitForExit();
        ...
        byte[] tosend = Encoding.ASCII.GetBytes(output);
        netstream.Write (tosend, 0, output.Length);

        // client runs 'client.Close()' after receiving this text
        // so the server should close this connection automatically too.
        // Adding a 'client.Close();' statement here made no difference.
    }
    // - catch and write exceptions to console, closing stream
    catch (Exception ex) {
        Console.WriteLine(ex.Message);
        netstream?.Close();
    }
}                                       // for ever
// I wonder if I should (could) close something here if the
// code breaks out of the loop, but everything works fine so
// I have no code here.

 return 0;

0 个答案:

没有答案