c#异步串口读取

本文关键字:读取 串口 异步 | 更新日期: 2023-09-27 18:06:35

我有一个类,使用c#中的DataReceived事件处理程序从串行端口读取。当我接收数据时,我知道头将有5个字节,所以我不想对数据做任何事情,直到我至少有。我当前的代码如下:

while (serialPort.BytesToRead<5)
{
//Do nothing while we have less bytes than the header size
}
//Once at least 5 bytes are received, process header

据我所知,这段代码是阻塞的,需要改进。我在征求如何做这件事的建议。DataReceived事件处理程序中的另一个事件处理程序是否合适?

c#异步串口读取

使用异步编程(不要忘记首先将应用程序定位于。net Framework 4.5)

这是我的实现作为SerialPort的扩展方法。

using System;
using System.IO.Ports;
using System.Threading.Tasks;
namespace ExtensionMethods.SerialPort
{
    public static class SerialPortExtensions
    {
        public async static Task ReadAsync(this SerialPort serialPort, byte[] buffer, int offset, int count)
        {
            var bytesToRead = count;
            var temp = new byte[count];
            while (bytesToRead > 0)
            {
                var readBytes = await serialPort.BaseStream.ReadAsync(temp, 0, bytesToRead);
                Array.Copy(temp, 0, buffer, offset + count - bytesToRead, readBytes);
                bytesToRead -= readBytes;
            }
        }
        public async static Task<byte[]> ReadAsync(this SerialPort serialPort, int count)
        {
            var buffer = new byte[count];
            await serialPort.ReadAsync(buffer, 0, count);
            return buffer;
        }
    }
}

和这里如何阅读:

public async void Work()
{
   try
   {
       var data = await serialPort.ReadAsync(5);
       DoStuff(data);
   }
   catch(Exception excepcion)
   {
       Trace.WriteLine(exception.Message);
   }
}

这会烧掉100%的core,你不会想那样做的。正确的方法是让程序阻塞Read()调用。你可以这样写:

private byte[] rcveBuffer = new byte[MaximumMessageSize];
private int rcveLength;
void ReceiveHeader() {
    while (rcveLength < 5) {
        rcveLength += serialPort.Read(rcveBuffer, rcveLength, 5 - rcveLength);
    }
}

或者如果你使用DataReceived事件,那么它看起来像这样:

    private void serialPort_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e) {
        if (e.EventType != System.IO.Ports.SerialData.Chars) return;
        if (rcveLength < 5) {
            rcveLength += serialPort.Read(rcveBuffer, rcveLength, 5 - rcveLength);
        }
        if (rcveLength >= 5) {
            // Got the header, read the rest...
        }
    }

不要忘记在您获得整个消息并处理它之后将rcveLength设置回0。