我使用NetworkStream带TcpClient。
首先我设置我的 tcp 客户端:
tcp = new TcpClient(AddressFamily.InterNetwork)
{ NoDelay = true, ReceiveTimeout = 5000};
Run Code Online (Sandbox Code Playgroud)我的主要数据接收循环:
while (true)
{
//read available data from the device
int numBytesRead = await ReadAsync();
Console.WriteLine($"{numBytesRead} bytes read"); //BP2
}
Run Code Online (Sandbox Code Playgroud)和实际的 TCP 数据读取:
public Task<int> ReadAsync()
{
var stream = tcp.GetStream();
return stream.ReadAsync(InBuffer, 0, InBuffer.Length); //BP1
}
Run Code Online (Sandbox Code Playgroud)我将它连接到一个测试平台,让我可以发送手动数据包。通过设置断点和调试,我检查了stream.ReadTimeout从tcp.
如果我经常发送数据,它会按预期工作。但是如果我不发送任何数据,5 秒后似乎什么也没有发生,没有超时。我看到BP1在调试器中断点被击中,但直到我从我的测试平台发送数据,才被BP2击中。我可以离开它一分钟或更长时间,它似乎只是在等待,但在一分钟后接收发送的数据,这似乎是不正确的行为。5 秒后肯定会发生一些事情(据我所知是一个例外)?
太晚了,所以我期待一些非常基本的东西,但谁能看到我的错误和解决方案?
好的,所以当我对我正在使用的实际 .Net 版本进行 RTFM 时(我有多少次被默认为 .Net Core 3 的 MS 抓住了,我确实说已经晚了)我在评论部分看到ReadTimeout:
此属性仅影响通过调用 Read 方法执行的同步读取。此属性不影响通过调用 BeginRead 方法执行的异步读取。
我现在还不清楚,如果我可以使用现代awaitable调用所有安全和使用专门超时读取套接字数据。除了超时,它正在工作,但我不确定给定如何ReadAsync在NetworkStream. 我必须做一些丑陋的黑客还是有一个简单的解决方案?
在我的情况下,5000 是我在断定存在问题之前不接收数据的最长预期 - 该协议没有 ping 机制,因此如果没有出现任何内容,我认为连接已死。因此,认为具有 5000 毫秒超时的异步读取会很好且整洁。
网络对象的超时值仅适用于同步操作。例如,来自文档:
此选项仅适用于同步 Receive 调用。
对于Socket.ReceiveTimeout、TcpClient.ReceiveTimeout、 和NetworkStream.ReadTimeout,这些实现最终都会导致调用SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, ...),进而有效地调用本机setsockopt()函数。从该文档:
SO_RCVTIMEODWORD设置超时(以毫秒为单位)以阻止接收调用。
(强调我的)
正是底层本机 API 中的这种限制是托管 API 中相同限制的原因。超时值不适用于网络对象上的异步 I/O。
您需要自己实现超时,方法是在超时发生时关闭套接字。例如:
async Task<int> ReadAsync(TcpClient client, byte[] buffer, int index, int length, TimeSpan timeout)
{
Task<int> result = client.GetStream().ReadAsync(buffer, index, length);
await Task.WhenAny(result, Task.Delay(timeout));
if (!result.IsCompleted)
{
client.Close();
}
return await result;
}
Run Code Online (Sandbox Code Playgroud)
可以在其他相关问题中找到有关此主题的其他变体:
NetworkStream.ReadAsync with an cancel token never
cancels Cancel C# 4.5 TcpClient ReadAsync by timeout
关闭套接字实际上就是您所能做的。即使对于同步操作,如果发生超时,套接字将不再可用。没有可靠的方法来中断读取操作并期望套接字保持一致。
当然,您可以选择在关闭套接字之前提示用户。但是,如果您要这样做,您将在应用程序架构中的更高级别实现超时,这样 I/O 操作本身就完全没有超时意识。
| 归档时间: |
|
| 查看次数: |
403 次 |
| 最近记录: |