coap组包格式的简单解析

最近的一个项目中用到了coap,为了防止时间过长导致遗忘,将具体的关键知识写在这里,同时也为后来人减少一些坑。

Coap

coap是一种符合REST规范(主要是幂等性)的,适用于物联网通讯的数据协议。具体使用起来和http比较类似,同样有着GETPOSTPUTDELETE四种操作,不过coap是基于udp的,并且数据包是按字节码拼接的,甚至有些信息是按位标识的,不像是http一样用各种字符串来表达各个部分。

省流量、计算少、适合物联网,但是在当前ipv6没有普及之时,实时性不如mqtt。


coap数据包格式的资料

在查找相关资料时,发现官方的资料长达一百多页(https://tools.ietf.org/html/rfc7252),虽然很全面,但是我并不需要如此详细的文档,另外还不知道coap基本长啥样呢。

好在我在GitHub上找到了一个国外网友制作整理的备忘笔记,简单明了,可以在https://github.com/chenxuuu/coap-cheatsheet/blob/master/coap-cheatsheet.md查看我fork并转为markdown格式的这个文档

我就基于这个笔记,来简单解释一下整个coap数据包的格式,并在最后实际测试一下几个实例数据包。

数据包的整体结构

coap的数据包格式类似于下面这样:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Ver| T |  TKL  |      Code     |          Message ID           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|   Token (if any, TKL bytes) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|   Options (if any) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|1 1 1 1 1 1 1 1|    Payload (if any) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

coap版本(Ver)

coap版本信息占用为2位(1/4字节)
现在的coap版本来说,固定是01

消息类型(T)

消息类型占用的大小位2位(1/4字节),类型如下四种:

具体数值消息类型名称
0CONfirmable
1NON-confirmable
2ACKnowledgement
3ReSeT

CON类型的消息为主动发出消息请求,并且需要接收方作出回复
NON类型的消息为主动发出消息请求,但是不需要接收方作出回复
ACK类型的消息为接收方作出回复
Res类型为发出CON消息后,在还没收到请求时,主动通知不需要再回复

token长度(token length,TKL)

token长度占用4位(0.5字节),表示后面的token所占的字节数。
可以为0,表示token不存在。

coap状态码(Code)

状态码占用1个字节,分为两种:发送和接收

发送方的状态码

状态码请求类型实际数值
0.00EMPTY0x00
0.01GET0x01
0.02POST0x02
0.03PUT0x03
0.04DELETE0x04

一般empty只在消息类型ReS的时候才会用到

响应方的状态码

类型:Success

CodeDescription
2.01 (65, 0x41)Created
2.02 (66, 0x42)Deleted
2.03 (67, 0x43)Valid
2.04 (68, 0x44)Changed
2.05 (69, 0x45)Content
2.31 (95, 0x5F)Continue

类型:Client Error

CodeDescription
4.00 (128, 0x80)Bad Request
4.01 (129, 0x81)Unauthorized
4.02 (130, 0x82)Bad Option
4.03 (131, 0x83)Forbidden
4.04 (132, 0x84)Not Found
4.05 (133, 0x85)Method Not Allowed
4.06 (134, 0x86)Not Acceptable
4.08 (136, 0x88)Request Entity Incomplete
4.12 (140, 0x8C)Precondition Failed
4.13 (141, 0x8D)Request Entity Too Large
4.15 (143, 0x8F)Unsupported Content-Format

类型:Server Error

CodeDescription
5.00 (160, 0xA0)Internal Server Error
5.01 (161, 0xA1)Not Implemented
5.02 (162, 0xA2)Bad Gateway
5.03 (163, 0xA3)Service Unavailable
5.04 (164, 0xA4)Gateway Timeout
5.05 (165, 0xA5)Proxying Not Supported

其中a.bb为响应代码,转成二进制的代码格式如下:

0
 0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
|class|  detail |
+-+-+-+-+-+-+-+-+

如2.31,拆开分成2进制为:
010 11111
转成16进制则为:0x5f

一般返回2.xx即为响应成功,其他一般都是失败,和http协议比较类似

消息编号(Message ID)

消息编号占用2字节,代表了该消息的编号,如果是CON类型的消息,在返回时消息编号也应当与发送时相同

Token

Token的字节数由之前的token长度(token length,TKL)决定,如果前面的TKL值是0,则数据包中不包含token

Options

Options占用的字节数不定,如果包尾遇到payload标识符0xff则表示Options数据结束

Options类似于http协议中的Options,有Content-FormatUri-Path之类的信息,格式解析与组包较为复杂,具体结构与其代表的数值如下:

  0   1   2   3   4   5   6   7
+---------------+---------------+
|  Option Delta | Option Length |   1 byte
+---------------+---------------+
/         Option Delta          /   0-2 bytes
          (extended)
+-------------------------------+
/         Option Length         /   0-2 bytes
          (extended)
+-------------------------------+
/         Option Value          /   0 or more bytes
+-------------------------------+
No.NameFormatLengthDefault
1If-Matchopaque0-8(none)
3Uri-Hoststring1-255(see note 1)
4ETagopaque1-8(none)
5If-None-Matchempty0(none)
7Uri-Portuint0-2(see note 1)
8Location-Pathstring0-255(none)
11Uri-Pathstring0-255(none)
12Content-Formatuint0-2(none)
14Max-Ageuint0-460
15Uri-Querystring0-255(none)
17Acceptuint0-2(none)
20Location-Querystring0-255(none)
28Size2uint0-4(none)
35Proxy-Uristring1-1034(none)
39Proxy-Schemestring1-255(none)
60Size1uint0-4(none)

Content-Formats参数的具体数值:

Media typeId.
text/plain;charset=utf-80
application/link-format40
application/xml41
application/octet-stream42
application/exi47
application/json50
application/cbor60

Option Delta

Option Delta占用4位(0.5字节)

Option Delta代表Option的类型,该值代表了上表中Option类型的代码值与上一个Option代码值之间的差值
(如果该Option为第一个Option,则直接表达该OptionOption Delta

由于Option Delta只有4位,最大只能表达15,为了解决这个问题,coap协议有着如下规定:

  • Option Delta号码<=12时:Option Delta位为实际的Option Delta
  • Option Delta号码<269时:Option Delta位填入13;并且在后面的Option Delta(extended)位会占用1字节,并且填入的数为实际Option Delta值减去13
  • Option Delta号码<65804时:Option Delta位填入14;并且在后面的Option Delta(extended)位会占用2字节,并且填入的数为实际Option Delta值减去269

特别注意,填入的Option Delta值不可能为15(0x0f)当遇到15时,该包无效

Option Length

Option Length占用4位(0.5字节)

Option Length代表该option所包含数据(value)的长度,该值的表示方法类似于Option Delta,如下:

  • Option Length号码<=12时:Option Length位为实际的Option Length
  • Option Length号码<269时:Option Length位填入13;并且在后面的Option Length(extended)位会占用1字节,并且填入的数为实际Option Length值减去13
  • Option Length号码<65804时:Option Length位填入14;并且在后面的Option Length(extended)位会占用2字节,并且填入的数为实际Option Length值减去269

填入的Option Length值不可能为15,当遇到15时,该包无效

Option Delta(extended)和Option Length(extended)

Option Delta(extended)Option Length(extended)的意义在上面已经解释过了,在不需要这两个值的情况下,这两个部分的数据便不存在

多个option的情况

当有多个option时,这些option必须是按option代码值(No.)的顺序**从小到大**排列的,不然会导致Option Delta的值出错
每个部分的option各自按格式组包,最后按顺序拼到一起,并入字节流中

负载内容(Payload)

Payload占用字节数不定

Payload不存在时,数据包末尾不能加上0xff的分隔符

如果存在0xff的分隔符,则分隔符后的数据便是Payload

解释结束,举个实际例子

在网上有一个coap测试服务器,域名为coap.me

我们假设一个例子,用串口工具将这个例子发出

假设需要请求coap.me,5683端口的coap数据,GET请求,网址如下:

coap://coap.me:5683/path/sub1

首先不管服务器,先分析一下数据包格式:

版本号:01(二进制)
消息类型:00(二进制,表示CON)
token长度:0010(二进制,表示2字节)
请求码:0x01(GET)
消息id:0x1234(随便编的)
token:0x5678(随便编的)

到了Options这里,我们就要再小小分析一下了,这个请求有两个Options
Uri-Pathpath
Uri-Pathsub1

所以数据包格式接着上面:

Option Delta:1011(二进制,表示编号11)
Option Length:0100(二进制,表示4位数据)
Option Delta(extended):空
Option Length(extended):空
Option Value:0x70 61 74 68(字符串path

Option Delta:0000(二进制,表示上一个编号11 + 0)
Option Length:0100(二进制,表示4位数据)
Option Delta(extended):空
Option Length(extended):空
Option Value:0x73 75 62 31(字符串sub1

该请求没有payload,所以也就不去构造payload了

数据完整的16进制代码为:

42 01 12 34 56 78 B4 70 61 74 68 04 73 75 62 31

我们用工具测试一下:

收到的回复为:

62 45 12 34 56 78 48 CB B0 EF 05 63 11 E3 84 80 FF 54 44 5F 43 4F 52 45 5F 43 4F 41 50 5F 30 39 20 73 75 62 31

解包分析一下:

01:协议版本号
10:消息类型,ACK
0010:token长度2字节
0x45:响应代码2.05,Content
0x12 34:消息id
0x56 78:token内容
0x48:option类型ETag,8字节
0xCB B0 EF 05 63 11 E3 84:ETag内容
0x80:option类型Content-Format,0字节
0xFF:和payload消息的分隔符
0x54 44 5F 43 4F 52 45 5F 43 4F 41 50 5F 30 39 20 73 75 62 31:payload内容,解码结果为TD_CORE_COAP_09 sub1

分析完毕,下一篇文章就可以讲解用lua代码来进行组包与拆包了

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注