【问题标题】:NetworkStream Async Read -> CancelNetworkStream 异步读取 -> 取消
【发布时间】:2020-03-13 18:34:51
【问题描述】:

目前我尝试从网络流中读取和写入异步。我的软件是客户端部分,服务器可以自己发送信息或响应我发送给他的命令。

所以我需要一个插座

  • 一直读取(以防服务器发送状态信息)
  • 当我想发送命令时停止读取(命令可以是具有多个写入和读取操作的数据序列)

所以我认为创建一个信号量和一个后台任务来处理服务器发送的消息是一个很好的方法,如果我想发送一个命令,我会阻止信号量并拥有对插座。

这是我目前所做的。

private TcpClient _tcpClient = new TcpClient();
protected SemaphoreSlim ClientSemaphore { get; } = new SemaphoreSlim(1, 1);

public async Task ConnectAsync()
{
    if (_tcpClient.Connected)
    {
        await DisconnectAsync();
    }
    await _tcpClient.ConnectAsync(Hostname, RemotePort);

    //here the background Task is started
    _ = AutoReceiveMessages();
}

private async Task AutoReceiveMessages()
{
    while (_tcpClient.Connected)
    {
        //enter and lock semaphore
        await ClientSemaphore.WaitAsync();
        try
        {
            //read from socket until timeout (ms)
            var msg = await ReadFromSocket(2000);
            foreach (var cmd in SplitMessageInTelegrams(msg))
            {
                Console.WriteLine("MESSAGE --> " + cmd);                    
            }
        }
        catch (Exception ex)
        {
        }
        finally
        {
            //release semaphore
            ClientSemaphore.Release();
        }
    }
}

private async Task<string> ReadFromSocket(double timeout = 0)
{
    var buf = new byte[4096];
    var stream = _tcpClient.GetStream();

    //read from stream or timeout
    var amountReadTask = stream.ReadAsync(buf, 0, buf.Length);
    var timeoutTask = Task.Delay(TimeSpan.FromMilliseconds(timeout));

    await Task.WhenAny(timeoutTask, amountReadTask)
              .ConfigureAwait(false);

    //timeout
    if (!amountReadTask.IsCompleted)
    {
        throw new TimeoutException("Timeout");
    }

    //no timeout
    return Encoding.ASCII.GetString(buf, 0, amountReadTask.Result);
}

但这并没有像我预期的那样工作...... 我使用这种方法向服务器发送消息在 WireShark 中我看到服务器响应相同的消息

protected async Task SendTelegramAsync(ITelegram telegram)
{
    await ClientSemaphore.WaitAsync();
    try
    {
        _ = telegram ?? throw new ArgumentException($"{nameof(telegram)}");
        if (!_tcpClient.Connected) throw new InvalidOperationException("Socket not connected!");

        var buf = new byte[4096];
        var stream = _tcpClient.GetStream();
        var msg = Encoding.ASCII.GetBytes("\x02" + telegram.GetCommandMessage() + "\x03");

        Console.WriteLine("WRITE --> " + msg);
        await stream.WriteAsync(msg, 0, msg.Length);

        //comment AutoReceiveMessage and remove comment from this
        //and I get responses from the server
        //var test = await ReadFromSocket(2000);
    }
    finally
    {
        ClientSemaphore.Release();
    }
}

我知道在这种情况下我不需要信号量,但稍后我想创建序列,因此一个命令包含多个写入和读取,并且只要执行该命令,我就不想使用 AutoReceiveMessages 方法。

现在的问题是

  • 如果我这样使用它,我永远不会得到响应,即使wireshark 告诉我服务器已响应,ReadFromSocket 方法也总是会超时
  • 但如果我禁用 AutoReceiveMessages(只需注释 _ = AutoReceiveMessages())并直接在 SendTelegramAsync() 中使用 ReadFromSocket,一切都会按预期工作会更好。

所以我认为问题与后台任务和 ReadAsync 有关,但我无法弄清楚...

【问题讨论】:

    标签: c# .net sockets


    【解决方案1】:

    知道了!

    stream.DataAvailable 是你的朋友(或我的朋友 :))。

    如果我在 ReadAsync 之前检查 DataIsAvailable,那么我就没有问题了。

    if (_tcpClient.GetStream().DataAvailable)
        var msg = await ReadFromSocket(DEFAULT_TIMEOUT);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-12-29
      • 1970-01-01
      • 1970-01-01
      • 2011-10-31
      • 1970-01-01
      • 1970-01-01
      • 2014-10-11
      • 1970-01-01
      相关资源
      最近更新 更多