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

问题描述:

我想在.net中使用SerialPort类。SerialPort.BaseStream.ReadAsync缺少第一个字节

我选择保​​持我的服务异步,所以我使用SerialPort.BaseStream上的异步方法。

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

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

如果我在每次响应(读取)后关闭端口,并在执行新请求(写入)之前再次打开端口,则第一个字节不会丢失。但是,如果我在关闭后过早地打开端口,通常会导致"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; 
} 

有什么明显的错误在我这样做的方式是什么?有没有更聪明的方法来异步实现相同?

+0

在'WhenAny'或'WhenAll'后做'.Result'总让我感到畏缩,我知道它永远不应该有问题,但我仍然不喜欢它。我总是喜欢'bytesRead + = await responseTask',因为任务完成,所以'await'没有任何开销,但是你得到了抛出的解包异常的优点,而不是从'.Result'获得'AggregateException'如果你确实得到错误。 –

+0

您确定300 ms超时后流中没有字节吗?你怎么知道你错过了第一个字节?这是第一个字节在某种特定方面?我问的原因是,也许前面的响应留下了一些字节,所以在第一次读取之后,每次读取实际上都包含第一个字节,但在前一个请求的剩余字节之后。您可能需要在每次请求前刷新流。 –

+0

我知道第一个字节缺失,因为每个响应应该以ASCII字符'$'(NMEA消息)开始。我现在已经切换到使用同步API,但在后台任务中运行它,它正在工作。虽然我不喜欢这个解决方案.. – Walkingsteak

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

你应该做的是通过使用CancellationToken来取消最后的ReadAsync()。请注意,超时和读取之间可能存在竞争,但我假设如果超时已过,无法在没有另外写入的情况下完成读取。

的代码应该是这样的:

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

注意,无论是原因和解决方法是我的猜测,这当然可能是我错了,大约一个或两个人。