1. Summary
COBS(Consistent Overhead Byte Stuffing)是一种算法,直译为一致的开销字节填充。简而言之,无论数据包的内容如何,都能通过产生高效可靠明确的数据包帧,从而使接受端能够从损坏的包中恢复。通常使用0x00
来作为数据包的分隔符,即切割数据包的片分隔符。当使用0x00
作为分隔符时,此算法会把每个零数据字节替换为非零值,这样数据包中就不会出现零数据字节,从而被误解为数据包分隔符。字节填充则是将可能包含"非法"或"保留"值,比如说数据包分隔符的数据字节序列,转换成不含这些值的序列的过程,当然可能会让这个序列变得更长,转换后的序列的额外长度通常被称为该算法的“开销”。关于开销有个例子,有个HDLC帧结构字节填充技术,大多数情况下开销很小,不到1%,但是最坏的情况下,数据包完全由需要转义的字节组成时,开销会达到100%。假设有一段数据需要通过HDLC帧结构传输,且该数据由一些特殊字节组成,这些字节需要进行转义处理:
-
原始数据:
7E 7D 7E
- 这里,
7E
是HDLC帧的结束标志,7D
是转义字符。
- 这里,
-
经过HDLC字节填充后的数据:
7D 5E 7D 5D 7D 5E
7E
被转义为7D 5E
,7D
被转义为7D 5D
,另一个7E
再次转义为7D 5E
。
在这种情况下,原始数据的长度是3个字节,但经过HDLC字节填充后,长度变成了6个字节,数据大小翻倍,开销达到了100%。
那么,COBS算法严格限制了最坏开销的情况,COBS最少需要一个字节的开销,对于长度为N的数据包,需要最大的(n/254)字节开销。因此,传输编码字节序列的时间是高度可预测的,这使得COBS对于可能存在抖动问题的实时应用程序非常有效。算法计算成本也是不高的,除了最坏情况的开销外,与HDLC这种算法相比也是要更低的。但是在传输他的第一个字节前,他必须要知道接下来254个字节中的第一个0字节。
2. Packet Framing And Stuffing
当分组数据通过串行协议发送时,需要一些协议来划分数据包的边界。这是通过使用帧标记来完成的,这是一个特殊的位序列或字符值,指示数据包之间的边界在哪里。数据填充是指在传输前对数据包数据进行转换以消除所有出现的分帧标记的过程,这样当接收方检测到标记时,就可以确定该标记表示数据包之间的边界。COBS将[0,255]范围内的任意字节串转换为[1,255]范围内的字节串。消除了数据中的所有零字节后,现在可以使用零字节明确地标记转换后数据的结束。这是通过向转换后的数据附加一个零字节来实现的,从而形成一个由cobs编码的数据(有效负载)组成的数据包,以明确地标记数据包的末尾。(可以保留任何其他字节值作为数据包分隔符,但使用零可以简化描述。)
COBS编码过程:
1.添加首个byte,这个byte指的是下一个0的位置,那么从上图可以看出,第三个位置是0,所以首个byte是03
2.而当数据中有0时,需要被改写,改写的值就是距离下一个0的距离,0距离最后一个00差2,所以改写为02
3.每一个数据包的最后都要加上00
3. Example
Example | Unencoded data (hex) | Encoded with COBS (hex) |
---|---|---|
1 | 00 | 01 01 00 |
2 | 00 00 | 01 01 01 00 |
3 | 00 11 00 | 01 02 11 01 00 |
4 | 11 22 00 33 | 03 11 22 02 33 00 |
5 | 11 22 33 44 | 05 11 22 33 44 00 |
6 | 11 00 00 00 | 02 11 01 01 01 00 |
7 | 01 02 03 … FD FE | FF 01 02 03 … FD FE 00 |
8 | 00 01 02 … FC FD FE | 01 FF 01 02 … FC FD FE 00 |
9 | 01 02 03 … FD FE FF | FF 01 02 03 … FD FE 02 FF 00 |
10 | 02 03 04 … FE FF 00 | FF 02 03 04 … FE FF 01 01 00 |
11 | 03 04 05 … FF 00 01 | FE 03 04 05 … FF 02 01 00 |
4. Python Code
由于工作需要需要支持cobs协议,所以上位机用python做了一个cobs的编解码器。借鉴了网上相关java的算法,采用的滑动窗口的方法,进行编解码。
Encode
def cobs_encoded(data_in):
encode_list = [0]
len_data = len(data_in)
zero_pos = 0
zero_adds = 1
# 复制输入数据到encode_list
encode_list.extend(data_in)
encode_list.append(0)
i = 1
while i < len(encode_list):
temp = encode_list[i]
if temp == 0:
encode_list[zero_pos] = zero_adds
zero_pos += zero_adds
zero_adds = 0
zero_adds += 1
if zero_adds >= 0xFF:
encode_list[zero_pos] = 0xFF
if i == len_data:
break
encode_list.insert(i + 1, 0)
i += 1
zero_pos += zero_adds
zero_adds = 1
i += 1
return encode_list
Decode
def cobs_decode(data):
# 将输入数据转换为列表
encode_list = list(data)
z_value = encode_list[0]
z_index = 0
i = 0
while i < len(encode_list):
temp = encode_list[i]
if z_index == z_value:
if z_value == 0xFF:
temp = encode_list[i] # 关键位置
encode_list.pop(i)
if i+1 <= len(encode_list) and encode_list[i+1] == 1:
encode_list[i] = 0
else:
encode_list[i] = 0
z_index = 0
z_value = temp
z_index += 1
i += 1
# 移除第一位包头
encode_list.pop(0)
# 移除结尾的0(如果存在)
if encode_list and encode_list[-1] == 0x00:
encode_list.pop()
return encode_list
Test Result
Example | Unencoded data (hex) | Encoded with COBS (hex) | Encode Result | Decode Result |
---|---|---|---|---|
1 | 00 | 01 01 00 | Pass | Pass |
2 | 00 00 | 01 01 01 00 | Pass | Pass |
3 | 00 11 00 | 01 02 11 01 00 | Pass | Pass |
4 | 11 22 00 33 | 03 11 22 02 33 00 | Pass | Pass |
5 | 11 22 33 44 | 05 11 22 33 44 00 | Pass | Pass |
6 | 11 00 00 00 | 02 11 01 01 01 00 | Pass | Pass |
7 | 01 02 03 … FD FE | FF 01 02 03 … FD FE 00 | Pass | Pass |
8 | 00 01 02 … FC FD FE | 01 FF 01 02 … FC FD FE 00 | Pass | Pass |
9 | 01 02 03 … FD FE FF | FF 01 02 03 … FD FE 02 FF 00 | Pass | Pass |
10 | 02 03 04 … FE FF 00 | FF 02 03 04 … FE FF 01 01 00 | Pass | Pass |
11 | 03 04 05 … FF 00 01 | FE 03 04 05 … FF 02 01 00 | Pass | Pass |
github上附有对应的测试用例数据,可以看下,觉得有用可以帮忙star下~
reference
https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing wiki百科官方解释
标签:02,编解码,01,字节,python,demo,list,encode,数据包 From: https://blog.csdn.net/weixin_44300062/article/details/140690981