通过超时取消C#4.5 TcpClient ReadAsync

时间:2013-03-07 14:29:49

标签: c# timeout tcpclient

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

TcpClient.ReadTimeout似乎应用于同步只读。

更新:
Tried tro应用这里描述的方法Cancelling an Asynchronous Operation

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

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

4 个答案:

答案 0 :(得分:12)

所以我知道这是很长一段时间 但谷歌还是把我赶到了这里 我看到没有标记为答案

对我来说,我这样解决 我做了一个扩展,添加了一个额外超时的方法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表示读取超时

答案 1 :(得分:3)

编辑:以下要点是我为此所做的解决方法。 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调用似乎会泄漏,在我的情况下,它似乎读取稍后的写入,并且数据基本上在内存中不再使用的地方返回。

答案 2 :(得分:1)

CancellationToken传递给ReadAsync()并不会带来太多价值,因为它仅在 it starts reading之前检查令牌的状态:

// 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()或timout任务完成,您可以很容易地自己添加一个timout机制:

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}");
}

答案 3 :(得分:0)

你必须自己实现它:它在那个类中不可用。

你应该使用一个定时器,它会在一段时间后终止异步操作,或者在后台线程中使用读同步版本。

你可以找到一个例子:Implementing a timeout with NetworkStream.BeginRead and NetworkStream.EndRead