自定义通信协议#

1.自定义协议简介#

一般自定义通信协议格式如下:

  • 格式一:用帧头和帧长度来确定一个完整的帧
帧头 帧长度 数据 校验
0xFF len data crc
  • 格式二:用帧头和帧尾来确定一个完整的帧
帧头 数据 校验 帧尾
0xFF data crc 0xFE

在协议中 <帧头与帧尾属于特殊字节>,如果在 “数据区域” 出现了帧头和帧尾,就可能造成数据解析错误。

格式二出现错误的概率要远高于格式一,因为对于格式二只要数据区域出现0xFE就被接收方认为已经接收到一帧数据;对于格式一来说,除非数据传输错误,就算是数据区域出现0xFF依然会被正确解读。

最安全的做法是将通信协议中的特殊字节进行转义处理。步骤如下:

  • 选择一个字节作为转义字节,转义字节也属于特殊字节
  • 设计转义规则

2. 转义实现#

eg1:#

  • 选择0xFD(除了特殊字节其他应该都可以)作为转义字节
  • 制定转义规则:将特殊字节减去0x20

数据发送方遇到特殊字节0xFF,就将0xFF-0x20=0xDF,并在其前面添加转义字节0xFD;数据接收方遇到0xFD就丢弃,并将下一个字节加上0x20作为接收数据。

特殊字节 转义后
0xFF 0xFD 0xDF
0xFD 0xFD 0xDD

eg2:#

  • 选择0xFD(除了特殊字节其他应该都可以)作为转义字节
  • 制定转义规则:将特殊字节异或0x20

数据发送方遇到特殊字节0xFF,就将0xFF^0x20=0xDF,并在其前面添加转义字节0xFD;数据接收方遇到0xFD就丢弃,并将下一个字节^0x20作为接收数据。

特殊字节 转义后
0xFF 0xFD 0xDF
0xFD 0xFD 0xDD

3.收发代码#

  • 发送
if((data==0xFF)||(data==0xFD)){
    buf[index++]=0xFD;
    buf[index++]=data^0x20;
}
  • 接收
uint8_t esc_flag=0;

while(!queue_is_empty(&queue)){
    protocal_handle.ch = queue_out(&queue);

    /* get data */
    if(protocal_handle.ch==0xFD){
        esc_flag=1;
        return;
    }
    if(esc_flag==1){
        esc_flag=0;
        protocal_handle.ch ^=0x20;
    }
}