C# 读取刚刚打开的串行端口时,只读取一个字节

C# 读取刚刚打开的串行端口时,只读取一个字节,c#,serial-port,C#,Serial Port,奇怪的问题 当我从com端口读取时,如果数据到达,第一次调用只读取一个字节,忽略计数参数和超时内可用的字节数。所有进一步的读数都正常,只有第一个读数有问题 再次使用或关闭/打开(重新打开)com端口将导致首次读取出现问题 下面是一些代码: var port = new SerialPort(); port.PortName = "com2"; port.BaudRate = 9600; port.WriteTimeout = 1000; port.ReadTimeout = 1000; port

奇怪的问题

当我从com端口读取时,如果数据到达,第一次调用只读取一个字节,忽略
计数
参数和超时内可用的字节数。所有进一步的读数都正常,只有第一个读数有问题

再次使用或关闭/打开(重新打开)com端口将导致首次读取出现问题

下面是一些代码:

var port = new SerialPort();
port.PortName = "com2";
port.BaudRate = 9600;
port.WriteTimeout = 1000;
port.ReadTimeout = 1000;
port.Open();

// ... send some data first

var read = new byte[10];
if (port.Read(read, 0, read.Length) != read.Length)
{
    // always here at first reading no matter what
}

// ... send some data again

if (port.Read(read, 0, read.Length) != read.Length)
{
    // not here anymore (unless error)
}
调用
read()
时,如果指定数量的数据已可读取,则不会出现问题


一些解释

Read()
是同步读取。当接收到指定数量的数据或超时过期时,它将返回。看起来,
TimeoutException
只有在接收到零字节时才会抛出。若在读取请求的数据量之前超时,函数将返回读取的字节数。因此,必须将返回值与请求值进行比较,以查看读取是否正常

但第一次打电话就有问题:

若0字节在超时期间到达,则第一个调用将正确等待超时并触发TimeoutException。若至少有1个字节已经到达,那个么函数将立即结束,忽略超时和要读取的字节数

这就是我的看法。我错了吗?我该怎么办


还有一些测试。插入
Thread.Sleep()
以确保调用
Read()
时,所有数据都可用,这将使问题消失


逻辑上,让我们只为第一次呼叫添加睡眠。耶。什么????问题现在出现在第二次呼叫仅第二次呼叫。换句话说,问题出现在第一次读取()时,因为在读取时,其他字节没有到达,所以它将只读取一个字节,因为队列中只有一个字节,所以它不会提供所有数据,而只能读取一次。所以,如果至少有一个字节在那里,它将不会等待所需的字节数,因为超时已经发生,但它不会抛出和异常。如果没有,它将抛出超时异常。因此,您应该尝试读取,直到读取所需的字节数(在您的情况下为10个字节)。您可以尝试以下方法:

int count = 0;
var read = new byte[10];
while ((count += port.Read(read, count, read.Length - count)) != read.Length) ;
但如果超时发生,可能会加强代码:

int count = 0;
var read = new byte[10];
while (count < read.Length)
{
    try
    {
        count += port.Read(read, count, read.Length - count);
    }
    catch (TimeoutException te)
    {
        //maybe increase ReadTimeout or something, use exponential backoff, your call
    }
}
int count=0;
var read=新字节[10];
while(计数<读取长度)
{
尝试
{
count+=port.Read(Read,count,Read.Length-count);
}
捕获(TimeoutException te)
{
//可能会增加读取超时或其他,使用指数退避,您的呼叫
}
}

有几种方法可以解决此问题。如果您希望收到您发送的命令的答复,则在调用serialPort.Write()之后,应在调用serilPort.Read()之前休眠至少250毫秒。或者,如果您知道设备发送回的字节数,您可以使用serialPort.BytesToRead属性延迟读取,直到您期望的字节数可用为止。如果您使用的是DataReceived事件,则可以设置serialPort.ReceivedBytesThreshold(默认值为1),以防止在预期字节数可用之前引发事件。请记住,serialPort.Read方法不能保证返回请求的字节数。这就是serialPort.Read方法返回一个整数的原因,该整数是实际读取的字节数。

我找到了我的解决方案,这有助于:

ireadCount = serialPort.Read(readBuffer, 0, serialPort.ReadBufferSize);
Thread.Sleep(1);//This can be removed, just use it in case need time to complete the reception.
ireadCount = serialPort.Read(readBuffer, 1, serialPort.ReadBufferSize-1);
ireadCount +=1;
这样,我可以从外部设备获得整个响应,而不会丢失第一个字节


关于。-

有解决这个问题的方法。但是第一次调用时仍然存在一个问题,该调用只读取1字节,请参见问题编辑。请告诉我,为什么这只在第一次打电话时发生一次,以后再也不会发生?如果我以某种方式接受了第一个错误,那么在那之后它将开始正常工作。直到com端口重新打开(或调用
DiscardInBuffer()
)。为什么?根据设计?好的,使用较长数据包的测试表明您的想法似乎是正确的-始终合并来自peaces的数据包,因为
Read()
可以返回任意数量的读取字节。这应该在MSDN.Btw中提到,如果至少有一个字节在那里,它将不会等待所需的字节数,因为超时已经发生,但它不会抛出和异常-这是关键,超时没有发生。似乎
Read()
将等待超时时间到期,或者读取当前可用的字节。然后它又回来了。所以,若并没有字节,它只会阻塞,直到收到第一个字节,然后返回。似乎合乎逻辑。是的,没错,对不起,我刚才没看到你的问题。这就是重点,让它一直读到你读够为止。如果你不知道你将从设备接收多少字节,你可以做一个简单的循环(有延迟),查询可用字节数,并保持循环,直到结果停止改变。“
serialPort.Read
方法不能保证返回请求的字节数”,这是问题的核心。我在一年前就解决了这个问题(请参阅接受答案的注释)。顺便说一句,
serialPort
DataReceived
有一些严重的问题,我再也不会使用它了(仅在单独线程中进行硬核同步读取)。是的,SerialPort确实存在问题,SerialPort.Read不能保证按照您所说的读取请求的字节数。但我认为重要的是指出SerialPort.Read会以整数形式返回已读取的字节数。