【问题标题】:.NET Tcp Stream fails if idle more than three minutes如果空闲超过三分钟,.NET Tcp Stream 将失败
【发布时间】:2011-10-01 02:15:37
【问题描述】:

我有一个数据收集系统,它通过 TCP 连接将数据从收集计算机(服务器)传递到绘图计算机(客户端)。该代码在第一次运行集合时运行良好。如果系统空闲时间不超过三分钟,则代码在后续运行中继续正常工作。如果系统保持空闲超过 3 分钟,则传输会在前 12 秒左右挂起(长到足以导致收集缓冲区溢出)。

在每次收集开始时,客户端会进入 30 秒的循环,每 1 毫秒检查一次是否 _clientStream.DataAvailable = true。该循环看起来与此类似(删除了一些错误检查代码):

    public bool WaitForData(int maxWaitInMs)
    {
       DateTime start_time = DateTime.Now;

        while (!_clientStream.DataAvailable )
        {
            Thread.Sleep(DATA_WAIT_DELAY); //1 ms
            TimeSpan elapsed_time = DateTime.Now - start_time;
            if (elapsed_time.TotalMilliseconds > maxWaitInMs)
            {
                return false;
            }
        }
        return true;
    }

服务端只是在做固定长度数据的简单写入

 _dataTcpServer.SendData(packet_buffer, OFFSET, packet.Size());

当问题发生时,我可以从调试器中得知服务器调用了一次 SendData,返回获取下一个数据包再次调用 send 方法,然后在 SendData 调用上挂断大约 12 秒。与此同时,在 12 秒过去之前,客户端永远不会看到 DataAvailable 变为 true。发送超时保留为默认值,因此它应该是无限的。 12 秒的时间似乎确实与发送的数据量有关。

我在这个系统中做了不同的事情,不是在客户端应用程序启动时创建 TcpClient,而是在客户端需要连接时新建一个 TcpClient。在断开连接时,我处理通过 TcpCLient.GetStream 检索到的流并关闭 TcpClient。

我只是希望了解为什么闲置三分钟会产生这种影响(两个系统都运行 XP)。

编辑 - 问题已解决,但难以理解

在服务器上,我使用了一个单独的线程,该线程使用 TcpListener.AcceptTcpClient() 来允许客户端连接。 AcceptTcpClient() 返回一个 TcpClient 对象。我会定期检查 TcpClient 的 IsConnected 属性,以便知道客户端是否断开连接(即测量结束),然后我会再次挂起 AcceptTcpClient() 并等待下一次测量的开始。问题是即使客户端的 TcpClient 断开连接,服务器的 TcpClient.IsConnected 总是返回 true。

我通过让客户端向服务器发送消息(不是通过 TCP)然后服务器强制断开连接来解决问题。现在一切正常,但我确信我没有正确使用这个 API。虽然我可以看到异步接受的方法,但我看不到服务器应该如何找出客户端断开连接的时间,也看不到为什么服务器的 TcpClient.IsConnected 属性在不再存在时会返回 true有效的连接。

此外,我注意到 ExclusiveAddressUse 属性也很晚,所以我从未将其设置为 true。系统的行为就像在几分钟内建立了另一个客户端连接,重复使用相同的连接并且服务器可以向客户端发送数据(即,就好像客户端从未断开连接一样)。如果断开连接后超过三分钟,则接受新的客户端连接(尽管据我所知,我的代码从未回到 AcceptTcpClient() 行),但它不再是与服务器相同的连接继续发送。

【问题讨论】:

    标签: .net c#-4.0 tcpclient


    【解决方案1】:

    Thread.Sleep 在处理 IO 时是 EVIL。请改用socket.BeginReceive,如果仍然不起作用,请返回。

    【讨论】:

    • 为什么 Thread.Sleep 天生就是邪恶的?这是在一个与 UI 分开的线程中,系统的其余部分做的很少。您是否建议使用 Thread.Sleep 使系统不稳定或影响 TCP/IP 堆栈的工作方式?如果是这样,也许这可以解释我发现的实际问题。我将用今天的结果更新我的问题。
    • 轮询从来都不是一个好主意,因为它会无缘无故地占用一个新线程。当然。在客户端应用程序中运行良好,但在服务器应用程序中无法正常运行。
    • 尽管这不是我的问题的原因,但我很欣赏你的帖子。我阅读了有关异步方法的更多信息。他们似乎真的只是使用线程池,据我所知,可能相当于 Thread.Sleep(0) 我可以看到它们的使用如何使我的代码更清晰(更高效)。另外,您的帖子促使我了解 ManualResetEvent 并使用它而不是轮询方法。接下来就是自己直接开始使用ThreadPool了。
    【解决方案2】:

    虽然我仍然不明白为什么当我的服务器没有在 AcceptTcpClient 上挂起时我的客户端可以连接,但我想我确实理解为什么即使客户端断开连接,Connected 也会返回 true。我深入研究了 MSDN 上的底层 Socket 文档并看到了这个:

    Connected 属性的值反映了最近一次操作时的连接状态。如果您需要确定连接的当前状态,请进行非阻塞、零字节的 Send 调用。

    由于客户端知道退出是因为服务器停止发送数据,所以客户端退出之前的最后一次发送是成功的。我认为 Connected 是这个属性的可怕名称。它应该是 WasConnected 或 WasConnectedAtLastSend,虽然更冗长,但它会为我省去很多心痛(因此可以更快地阅读 MSDN 文档)。

    【讨论】:

      猜你喜欢
      • 2023-03-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多