我正在尝试在.net中使用SerialPort类。
我选择保持我的服务异步,所以我在SerialPort.BaseStream
上使用异步方法。
在我的异步方法中,我将一个 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;
}
我这样做的方式有什么明显的问题吗?有没有更聪明的方法可以异步实现相同的目标?
仅仅因为您没有观察到最后一个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);
请注意,原因和解决方案都是我的猜测,当然有可能我对其中一个或两个都错了。