SerialPort.BaseStream.ReadAsync缺少第一个字节

时间:2016-03-08 15:01:48

标签: c# asynchronous stream serial-port async-await

我正在尝试在.net中使用SerialPort类。

我选择保持服务异步,所以我在SerialPort.BaseStream上使用async-methods。

在我的异步方法中,我将一个byte []写入串口,然后开始读取,直到我在n毫秒内没有收到任何数据,并返回该结果。

但问题是,除了打开串口后的第一个回复,我似乎错过了所有回复中的第一个字节。

如果我在每次响应(读取)后关闭端口,并在执行新请求(写入)之前再次打开它,则不会丢失第一个字节。但是,如果我尝试在关闭后过早打开端口,则通常会导致"Access to the port 'COM4' is denied."异常。对于每次写入/读取,打开/关闭似乎也是非常必要的。

这基本上就是我的方法:

private async Task<byte[]> SendRequestAsync(byte[] request)
{    
    // Write the request
    await _serialPort.BaseStream.WriteAsync(request, 0, request.Length);

    var buffer = new byte[BUFFER_SIZE];
    bool receiveComplete = false;
    var bytesRead = 0;

    // Read from the serial port
    do
    {
        var responseTask = _serialPort.BaseStream.ReadAsync(buffer, bytesRead, BUFFER_SIZE - bytesRead);
        if (await Task.WhenAny(responseTask, Task.Delay(300)) == responseTask)
        {
            bytesRead += responseTask.Result;
        }
        else
            receiveComplete = true;


    } while (!receiveComplete);

    var response = new byte[bytesRead];
    Array.Copy(buffer, 0, response, 0, bytesRead);

    return response;
}

我这样做的方式有什么明显的错误吗?是否有更智能的方法来异步实现相同的目标?

1 个答案:

答案 0 :(得分:3)

仅仅因为你没有观察到最后一个ReadAsync()并不意味着它被取消了,它仍然在运行,这显然表现在它读取以下消息的第一个字节。

您应该使用ReadAsync()取消上一个CancellationToken。请注意,超时和读取之间可能存在争用,但我假设如果超时已经过去,则无法在没有其他写入的情况下完成读取。

代码如下所示:

var cts = new CancellationTokenSource();

do
{
    var responseTask = _serialPort.BaseStream.ReadAsync(
        buffer, bytesRead, BUFFER_SIZE - bytesRead, cts.Token);

    if (await Task.WhenAny(responseTask, Task.Delay(300)) == responseTask)
    {
        bytesRead += responseTask.Result;
    }
    else
    {
        cts.Cancel();
        receiveComplete = true;
    }
} while (!receiveComplete);

请注意,原因和解决方案都是我的猜测,我当然可能对其中一个或两个都是错的。