我有一个数据收集系统,它通过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)。
编辑 - 解决了问题,但理解难以理解
在服务器上,我使用了一个单独的线程,该线程使用TcpListener.AcceptTcpClient()来允许客户端连接。 AcceptTcpClient()返回一个TcpClient对象。我会定期检查TcpClient的IsConnected属性,以便我知道客户端是否断开连接(即测量结束)然后我将再次挂起AcceptTcpClient()并等待下一次测量的开始。问题是即使客户端的TcpClient断开连接,服务器的TcpClient.IsConnected也总是返回true。
我通过让客户端向服务器发送消息(而不是通过TCP)解决了问题,然后服务器强制断开连接。现在一切正常但我确信我没有正确使用这个API。虽然我可以看到异步接受的方法,但我看不到服务器在客户端断开连接时应该如何找出,我不明白为什么服务器的TcpClient.IsConnected属性在不再存在时返回true有效连接。
我也注意到了SortAddressUse属性,所以我从未将其设置为true。系统的行为就像是在几分钟内建立了另一个客户端连接,重用了相同的连接,服务器可以向客户端发送数据(即,就好像客户端从未断开连接一样)。如果断开连接超过三分钟,则接受新的客户端连接(尽管我可以告诉我的代码永远不会返回到AcceptTcpClient()行),但它不再是服务器连接的相同连接发送。
答案 0 :(得分:1)
Thread.Sleep
是EVIL。请改用socket.BeginReceive
,如果仍然无效,请返回。
答案 1 :(得分:1)
虽然我仍然不明白为什么我的客户端可以在我的服务器未在AcceptTcpClient上挂起时连接但我想我明白为什么即使客户端断开连接,Connected也会返回true。我在MSDN上挖掘了底层的Socket文档并看到了这个:
Connected属性的值反映了最近一次操作时的连接状态。如果需要确定连接的当前状态,请进行非阻塞,零字节发送调用。
由于客户端知道要退出,因为服务器停止发送数据,因此在客户端退出之前的最后一次发送是成功的。我认为Connected是这个属性的可怕的名称。它应该是WasConnected或WasConnectedAtLastSend,虽然它会更加冗长,但它会让我心痛不已(因此很快就会阅读MSDN文档)。