超时取消c# 4.5 TcpClient ReadAsync

本文关键字:TcpClient ReadAsync 取消 超时 | 更新日期: 2023-09-27 18:02:51

在。net 4.5中通过超时取消TcpClient ReadAsync操作并捕获此超时事件的正确方法是什么?

TcpClient。ReadTimeout似乎适用于同步只读。

更新:
尝试应用这里描述的方法取消异步操作

var buffer = new byte[4096];
CancellationTokenSource cts = new CancellationTokenSource(5000);
int amountRead = await tcpClientStream.ReadAsync(buffer, 0, 4096, cts.Token);

,但它永远不会超时取消。有什么问题吗?

超时取消c# 4.5 TcpClient ReadAsync

所以我知道这是很久以前的事了但谷歌仍然驱使我来到这里我看到没有标记为答案

对于我,我是这样解的我做了一个扩展来添加一个方法ReadAsync,需要额外的超时
public static async Task<int> ReadAsync(this NetworkStream stream, byte[] buffer, int offset, int count, int TimeOut)
{
    var ReciveCount = 0;
    var receiveTask = Task.Run(async () => { ReciveCount = await stream.ReadAsync(buffer, offset, count); });
    var isReceived = await Task.WhenAny(receiveTask, Task.Delay(TimeOut)) == receiveTask;
    if (!isReceived) return -1;
    return ReciveCount;
}

所以如果它返回-1表示读取超时

编辑:以下要点是我为此做的一个解决方案。https://gist.github.com/svet93/fb96d8fd12bfc9f9f3a8f0267dfbaf68

原始:

我真的很喜欢Khalid Omar的答案,并做了我自己的版本,我认为值得分享:

public static class TcpStreamExtension
{
    public static async Task<int> ReadAsyncWithTimeout(this NetworkStream stream, byte[] buffer, int offset, int count)
    {
        if (stream.CanRead)
        {
            Task<int> readTask = stream.ReadAsync(buffer, offset, count);
            Task delayTask = Task.Delay(stream.ReadTimeout);
            Task task = await Task.WhenAny(readTask, delayTask);
            if (task == readTask)
                    return await readTask;
        }
        return 0;
    }
}

它或多或少是相同的事情,除了以一种更可读的方式(对我来说)格式化略有不同,更重要的是它不使用Task.Run,我不确定为什么在他的例子中使用它。

编辑:

上面的内容乍一看似乎不错,但我发现它会引起一些问题。如果没有数据传入,readAsync调用似乎会泄漏,在我的情况下,它似乎读取后来的写入,并且数据基本上在不再使用的内存中返回。

CancellationToken传递给ReadAsync()并没有增加多少价值,因为它只在开始读取

之前检查令牌的状态:
// If cancellation was requested, bail early with an already completed task.
// Otherwise, return a task that represents the Begin/End methods.
return cancellationToken.IsCancellationRequested
            ? Task.FromCanceled<int>(cancellationToken)
            : BeginEndReadAsync(buffer, offset, count);

通过等待ReadAsync()或超时任务完成,您可以很容易地自己添加一个超时机制,如下所示:

byte[] buffer = new byte[128];
TimeSpan timeout = TimeSpan.FromSeconds(30);
var readTask = stream.ReadAsync(buffer, 0, buffer.Length);
var timeoutTask = Task.Delay(timeout);
await Task.WhenAny(readTask, timeoutTask);
if (!readTask.IsCompleted)
{
    throw new TimeoutException($"Connection timed out after {timeout}");
}

编辑:好的,这就锁定了一个线程。但我认为,对于许多应用来说,这仍然是一个简单的解决方案。

好吧,这是一个旧的线程,但我想我应该分享我自己的解决方案,因为它看起来更简单。

而不是使用e.g.

int length = await stream.Read(buffer, 0, 2, cancellationToken);

简单地使用

int length = 0;
await Task.Run(() => { length = stream.Read(buffer, 0, 2); });

,这尊重流。