【问题标题】:How to read all the bytes from Serialport as indicated by BytesToRead如何从 BytesToRead 指示的串行端口读取所有字节
【发布时间】:2017-05-21 14:04:11
【问题描述】:

我正在尝试从仪表上读取数据。圣杯是设备实际支持的1000 samples per second。波特率是 38400, Parity.None 和 Stopbits.One 如果重要的话。我正在使用binary mode 让事情尽可能快。我计划如下使用 DataReceived 事件。

private void serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        int bytesToRead = _serialPort.BytesToRead;
        byte[] data = new byte[bytesToRead];
        int actualBytesRead = 0;
        do
        {
            actualBytesRead = serialPort.Read(data, 0, bytesToRead);
        } while (actualBytesRead != bytesToRead);

        //At this point assume that the data byte array has all the data
    }

似乎BytesToRead 将返回该事件可读取的所有字节。 但是正如link 所说

The SerialPort class buffers data, but the stream object contained in the
SerialPort.BaseStream property does not. Therefore, the SerialPort object
and the stream object might differ on the number of bytes that are 
available to read. When bytes are buffered to the SerialPort object, the 
BytesToRead property includes these bytes in its value; 
however, these bytes might not be accessible to the stream contained in
the BaseStream property. 

而Read 仅返回它已读取的字节数。 因此,作为预防措施,我计划持续读取,直到读取的字节数与 BytesToRead 为引发的事件指示的字节数相同。但是有几点我不清楚。

  • 这是否可行。我不确定一旦从串行端口的缓冲区中读取的数据是否会继续存在。
  • 如果不是,那么在循环时,我应该保持要读取的字节数等于 BytesToRead,还是应该根据已经从缓冲区读取的字节数调整它。
  • 我几乎一直在访问缓冲区,直到获得所有字节。这是正确的吗?这是否会导致锁定问题,因为字节需要更长的时间才能变得可用?
  • 有没有更好的方法来获取 BytesToRead 指示的所有字节?

【问题讨论】:

  • 注释很笨拙,请注意您链接到了错误的 MSDN 文章。 Read() 文章没有这个注释。区别与 Encoding 属性有关,当您读取字符串而不是字节时,它开始变得重要。就像使用 ReadExisting() 时所做的一样。 BytesToRead 总是告诉您可以读取的 bytes 数量,它不会告诉您可以读取的 characters 数量。所以不,那个 do-while 循环没有意义,它永远不会循环。
  • @HansPassant:修复了链接。串行端口article 确实提到了这一点:“因为 SerialPort 类缓冲数据,而 BaseStream 属性中包含的流没有,所以两者可能会在有多少字节可供读取方面发生冲突。BytesToRead 属性可以指示有字节要读取,但 BaseStream 属性中包含的流可能无法访问这些字节,因为它们已缓冲到 SerialPort 类。"。当 BytesToRead 指示的所有字节都可用时?

标签: c# serial-port


【解决方案1】:

以下是错误的:

int actualBytesRead = 0;
do
{
    actualBytesRead = serialPort.Read(data, 0, bytesToRead);
} while (actualBytesRead != bytesToRead);

在循环的每次迭代中,您始终为 offset 传递 0,因此每次读取字节时,您都会覆盖之前读取的字节。

我会改写如下:

for( int offset = 0;  offset < data.Length;  )
{
    int n = serialPort.Read( data, offset, data.Length - offset );
    offset += n;
}

【讨论】:

  • 这意味着一旦我设法读取了 n 个“可用”字节,它们就会从端口缓冲区中消失。我的印象是,无论我读了多少次,它们都会一直徘徊到下一个事件。因此,我想到了通过阅读整个内容并覆盖我以前阅读的内容来强制我的方式。
  • 是的,Read() 函数通常是这样工作的。但是 Hans Passant 可能是对的,如果你的 data 正好是 serialPort.BytesToRead 字节长,那么循环很可能只执行一次,它不会重复。
  • 这个语句:var bytesToRead = serialPort.BytesToRead 很有趣。调试器显示 bytesToRead=136 和 serialPort.BytesToRead=1867。几乎就好像在赋值之后 BytesToRead 值发生了变化。我不知道发生了什么。
  • @NotAgain 是的,我很怀疑。文档暗示可能会发生这样的事情,但目前还不清楚。我希望该事件能够重新发布,并且我认为您遇到的问题是它没有重新发布。但是,我不认为这应该是一个大问题。这意味着您需要继续执行serialPort_DataReceived() 中的代码,直到_serialPort.BytesToRead 返回零。这意味着您将留下许多 data 数组,您需要弄清楚如何处理它们。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-17
  • 2013-10-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多