首页 文章

如果空闲超过三分钟,.NET Tcp Stream会失败

提问于
浏览
0

我有一个数据收集系统,通过TCP连接将数据从收集计算机(服务器)传递到绘图计算机(客户端) . 第一次运行集合时,代码工作正常 . 如果系统闲置超过三分钟,代码将继续在后续运行中正常工作 . 如果系统保持空闲状态超过三分钟,则传输将在前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秒 . 在此期间,客户端永远不会看到DataAvailable在12秒过后才会成功 . 发送超时保留为默认值,因此它应该是无限的 . 12秒的时间似乎与发送的数据量有些关系 .

我在这个系统中做了一些不同的事情,而不是在客户端应用程序启动时创建TcpClient,当客户端需要连接时,我新建了一个TcpClient . On Disconnect我处理通过TcpCLient.GetStream检索的Stream,然后关闭TcpClient .

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

EDIT - Problem Solved but understanding is proving elusive

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

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

另外我很晚才注意到ExclusiveAddressUse属性,所以我从来没有将它设置为true . 系统的行为就像是在几分钟内 Build 了另一个客户端连接,重复使用了相同的连接,服务器可以将数据发送到客户端(即,就好像客户端从未断开连接一样) . 如果断开连接超过三分钟,则接受新的客户端连接(尽管我可以告诉我的代码永远不会返回到AcceptTcpClient()行),但它不再是服务器连接的相同连接发送 .

2 回答

  • 1

    处理IO时 Thread.Sleep 是EVIL . 请改用 socket.BeginReceive ,如果仍然无效,请返回 .

  • 1

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

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

    由于客户端知道退出是因为服务器停止发送数据,因此在客户端退出之前的最后一次发送是成功的 . 我认为Connected是这个属性的 terrible 名称 . 它应该是WasConnected或WasConnectedAtLastSend,虽然它更加冗长会让我心痛不已(因此很快就会阅读MSDN文档) .

相关问题