【问题标题】:C# Setting WriteTimeout / ReadTimeout on Network Stream Makes No Difference?C# 在网络流上设置 WriteTimeout / ReadTimeout 没有区别?
【发布时间】:2019-02-27 01:46:54
【问题描述】:

我的 Xamarin.Forms 应用程序中有以下方法代码

using (TcpClient client = new TcpClient(ip, port))
using (NetworkStream stream = client.GetStream())
{
    byte[] messageBytes = PrepareMessageBytes();

    //
    // Setting these seam to have no effect
    //
    stream.WriteTimeout = (int)TimeSpan.FromSeconds(10).TotalMilliseconds;
    stream.ReadTimeout = (int)TimeSpan.FromSeconds(10).TotalMilliseconds;

    //
    // I have set read and write timeouts above but when 
    // hitting this line, app hangs indefinitely?  
    // I would expect that after 10 seconds, a IOException
    // will be thrown
    await stream.WriteAsync(messageBytes, 0, messageBytes.Length);
    stream.Flush();

    byte[] buffer = new byte[1024];
    StringBuilder builder = new StringBuilder();
    int bytesRead = 0;

    while (stream.DataAvailable)
    {
        bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
        builder.AppendFormat("{0}", Encoding.ASCII.GetString(buffer, 0, bytesRead));
    }

    string msg = receivedMessage.ToString();
} 

在上面,我已将写入和读取超时都设置为 10 秒。我知道我的代码在调用时挂起:

等待流。WriteAsync(...)

,但我预计 10 秒后会抛出 IOException。根据 MS 文档,异步写入/读取并非如此。

如何设置对 WriteAsync / ReadAsync 方法的调用超时?

更新 - 基于斯蒂芬的评论的第二次尝试

根据 Stephen 的评论,我更新了代码并将其包装到 try..catch 中以处理 OperationCancelledException。 但这导致现在比以前更多的问题,应用程序大部分时间仍然会挂起

try
{
    using (TcpClient client = new TcpClient(ip, port))
    using (NetworkStream stream = client.GetStream())
    using (var writeCts = new CancellationTokenSource(TimeSpan.FromSeconds(10)))
    {
        byte[] messageBytes = PrepareMessageBytes();

        await stream.WriteAsync(messageBytes, 0, messageBytes.Length, writeCts.Token);
        await stream.FlushAsync();

        byte[] buffer = new byte[1024];
        StringBuilder builder = new StringBuilder();
        int bytesRead = 0;

        while (stream.DataAvailable)
        {
            using (var readCts = new CancellationTokenSource(TimeSpan.FromSeconds(10)))
                bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, readCts.Token);
            builder.AppendFormat("{0}", Encoding.ASCII.GetString(buffer, 0, bytesRead));
        }

        string msg = receivedMessage.ToString();
    }
catch (OperationCancelledException ex)
{
    Debug.WriteLine(ex.Message);
}

【问题讨论】:

标签: c# timeout networkstream


【解决方案1】:

如何设置调用 WriteAsync / ReadAsync 方法的超时时间?

您使用带有CancellationToken 参数的重载:

using (TcpClient client = new TcpClient(ip, port))
using (NetworkStream stream = client.GetStream())
using (var writeCts = new CancellationTokenSource(TimeSpan.FromSeconds(10)))
{
    byte[] messageBytes = PrepareMessageBytes();

    await stream.WriteAsync(messageBytes, 0, messageBytes.Length, writeCts.Token);
    await stream.FlushAsync();

    byte[] buffer = new byte[1024];
    StringBuilder builder = new StringBuilder();
    int bytesRead = 0;

    while (stream.DataAvailable)
    {
        using (var readCts = new CancellationTokenSource(TimeSpan.FromSeconds(10)))
            bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, readCts.Token);
        builder.AppendFormat("{0}", Encoding.ASCII.GetString(buffer, 0, bytesRead));
    }

    string msg = receivedMessage.ToString();
}

我预计 10 秒后会抛出 IOException。

使用取消令牌时,应使用OperationCanceledException。从技术上讲,取消令牌可用于多种取消场景——代码决定不再需要完成操作、用户手动取消操作或发生超时。 OperationCanceledException 是泛型异常类型,意思是“已取消”。

【讨论】:

  • 谢谢斯蒂芬,所以将上面的代码包装到 try...catch 和 OperationCancellationException 应该会捕获取消异常,但我猜大多数情况下不会发生这种情况。当我运行应用程序时,我看到执行挂起。如果我正在调试,我确实看到我的 catch 块发出了取消异常,但不是在 10 秒后,它需要的时间比这更长。我也有一种感觉,应用程序现在挂在各个部分,但主要是调用 WriteAsync。有时我会通过这一点,但在第二次调用相同的方法时它会永远挂起。几乎就像某些东西没有被清除一样
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-15
  • 2020-04-09
  • 1970-01-01
  • 1970-01-01
  • 2016-11-09
  • 2010-09-21
相关资源
最近更新 更多