我使用TcpClient.BeginConnect
连接到客户端,只要不请求取消CancellationToken
,我就有一个循环。
while (!_token.IsCancellationRequested)
{
var s = reader.ReadLine() ?? string.Empty;
}
我还通过日志记录重新连接和处理异常。
当我启动此应用程序时,一切都按预期工作。但是,当我将计算机置于睡眠状态并再次唤醒它时,似乎所有线程都已终止。
工作流程如下:
- 我(从主线程)启动一个新任务,该任务执行
DoWork
。我这样执行:Task.Run(()=>DoWork(),_token);
DoWork
实例化一个新TcpClient
并启动如下所示的BeginConnect
:
(_client = new TcpClient()).BeginConnect(Address, Port, ConnectCallback, _client);
- 在
ConnectCallback
内,我有一个while语句,它不断从流中读取数据(见上文)。
知道计算机进入睡眠状态时线程会发生什么吗?
当您的计算机从睡眠状态唤醒时,连接有时会(但并非总是)被切断。这取决于许多因素,其中一些可能不受您的控制。
就回调方法而言,不会触发异常,但当连接结束且读取所有先前数据时reader.EndOfStream
异常将为真。编辑:但是,如果 TCP 堆栈不知道远程已断开连接,则此调用将阻止,直到数据到达、TcpClient.ReceiveTimeout
期已过或直到 TCP 会话空闲超时(以先发生者为准)。
(编辑:如果未发送任何数据,则仅当 TCP 堆栈知道(基于其自己的网络状态检测或 TCP 数据包)远程已断开连接时,连接才会自动结束;否则它将等到会话空闲超时,这可能需要长达一个小时左右,具体取决于客户端。一种解决方案是让客户端定期发送数据(如果它最近没有收到数据),以充当一种早期断开连接检测。
事实上,当连接结束时(例如,在您的计算机唤醒之后),您发布的while
循环将进入一个紧密循环,使CPU内核最大化,因为reader.ReadLine()
不断返回null
。您可以检查这一点并脱离循环:
if (reader.EndOfStream)
{
break;
}
这个问题的解决方案(虽然不是一个很好的解决方案)是使用一个计时器,该计时器在给定的时间量后显式关闭连接。
var timeoutTimer = new Timer(state =>
{
var temp = (TcpClient)state;
Log.Error("Read timeout. Closing connection.");
temp.Close();
}, client, Timeout.Infinite, Timeout.Infinite);
在访问流之前,我激活了此计时器:
timeoutTimer.Change(20000, Timeout.Infinite); // Handle timeouts.
然后重置它:
timeoutTimer.Change(Timeout.Infinite, Timeout.Infinite); // Reset timeout.
两者都使用阅读器。EndOfStream 或 reader。ReadLine 会导致线程在该点停止,除非强制终止连接,否则不会解析。
编辑:设置TcpClient.ReceiveTimeout与上述相同 - 可能更好。当接收方在指定的时间量(以毫秒为单位)内未收到任何数据时,引发 IOException。