C#中的SerialPort,数据接收不完整消息

时间:2013-02-01 01:04:03

标签: c# serial-port

我正在使用Serialport。我正面临一个新问题,一旦我收到数据,我的数据就不完整了。如何检查我的数据是否完整然后处理它们,如果没有,不处理它们?

以下是我的数据接收和发送功能:

 private void Send(byte[] cmd)
        {
            bResponse = new byte[0];
            Write(cmd);        
        }

 void comPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            int iCount = comPort.BytesToRead;
            byte[] bBuffer = new byte[iCount];
            comPort.Read(bBuffer, 0, iCount)
            if (bBuffer.Length == 1 && bBuffer[0] == ACK)
                Write(new byte[] { ENQ });
            else if (bBuffer.Length == 1 && bBuffer[0] == NAK)
            {
                Debug.WriteLine("Incomplete Message detected!");  
            }
            else
            {
                bResponse = bResponse.Concat(bBuffer).ToArray();
                    rResponse = Decode(bResponse);
                    Write(new byte[] { ACK });
            }
        }

我知道我的数据是在几个包中收到的,我需要等到响应完成,但我不知道基于上面的代码。我该如何检查数据是否完整以确定是否等待? (P.S:收到的答复的大小各不相同。)

3 个答案:

答案 0 :(得分:2)

没有完整性或数据包大小的内置概念。

您必须附加到缓冲区,直到您看到一些可识别的数据包结束模式(您或其他人)将其定义为协议规范的一部分。 - 如果你还没有看到你想要的东西,那么一段时间后可能会超时。

答案 1 :(得分:0)

旧项目的示例,注意firstindex,lastindex,你输入一个字符来知道长度,开始/结束字符是预定义的,可以是你选择的任何字符,只是一定不要采取任何常见字符

这适用于tcp / ip,但同样的原则可用于serialport

    public void ReceiveMessage(IAsyncResult ar) 
    {
        int bytesRead;
        try
        {
            lock (client1.GetStream()) 
            {
                bytesRead = client1.GetStream().EndRead(ar);
            }
            string messageReceived = System.Text.Encoding.ASCII.GetString(data, 0, bytesRead);
            received = messageReceived;
            int firstindex = received.IndexOf((char)2);
            int lastindex = received.IndexOf((char)3);
            if (firstindex > 0 && lastindex > 0)
            {
                string first = received.Substring(firstindex, 1);
                string last = received.Substring(lastindex, 1);
            }
            lock (client1.GetStream())
            {
                client1.GetStream().BeginRead(data, 0, System.Convert.ToInt32(client1.ReceiveBufferSize), ReceiveMessage, null);
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.ToString());
        }
    }

答案 2 :(得分:0)

我有一些代码给你。 首先,您实现DataReceived事件(就像您已经完成的那样)。仅在有数据要处理时才会调用此事件。虽然我不会称它为基于中断(如“实时能力”),但绝对不是轮询。哪个好。

第二:调用事件时,您可能只有一个字节,但可能有更多字节。要捕获每个数据包,您需要实现自定义缓冲区。

第三步:在缓冲区附加一个字节后,检查缓冲区是否包含有效数据包。如果是,请处理它。如果不是:追加另一个。如果没有剩余字节,则等待再次调用该事件。

在代码中它看起来像这样:

const BUFFERLENGTH = 17; // Bytes
byte[] buffer = new byte[BUFFERLENGTH];


private void COM_Port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    var port = (SerialPort)sender;
    while (port.BytesToRead > 0)
    {
        var data = (byte)port.ReadByte();
        Read(data);
    }
}

private void Read(byte value)
{
    // Append Byte to buffer
    System.Buffer.BlockCopy(buffer, 1, buffer, 0, BUFFERLENGTH- 1);
    buffer[BUFFERLENGTH - 1] = value;

    // Check for valid Packet
    if (IsValidPacket(buffer))
    {
        // Yeah! Gotcha :-)
        // Now copy your Packet from the Buffer to your struct/whatever
    }
}

private bool IsValidPacket(byte[] buffer)
{
    // Todo: Check whether buffer contains a valid Packet.
    // Some think like:
    // return buffer != null && buffer[0] == 0xF4 && buffer[2] == buffer.length
    throw new NotImplementedException();
}

请注意,我没有“将字节附加到缓冲区”。我丢弃了第一个字节,将每个字节移位一个位置并在最后插入新字节。如果找到有效的数据包,我可以将它在一个块中复制到结构中。因此缓冲区大小始终是恒定的,并且与一个数据包完全一样长。

这可能不是那里最快的代码(因为它分别读取每个字节),但它适用于我: - )

哦,如果你想在GUI中显示那些内容,请记得使用Begininvoke()。