0%

OpenHarmony|CoAP协议解读之adapter

本篇博客主要介绍CoAP中的coap_adapter部分。

首先是看到头文件部分(discovery/coap/include/coap_adapter.h)

1
2
3
4
5
6
7
8
typedef struct {
enum COAP_ProtocolTypeEnum protocol;
enum COAP_MsgTypeEnum type;
unsigned char code;
unsigned short msgId;
COAP_Option *options;
unsigned char optionsNum;
} COAP_PacketParam;

一开始先是定义了coap数据包的一些参数,例如

  • protocol 指的是使用TCP还是UDP
  • type 指的是消息是什么类型
    • 需要确认消息 CON
    • 不需要确认消息 NON
    • 确认应答消息 ACK
    • 复位消息 RST
  • 以及消息id、响应码、设置等

以及定义了一个数据读写的缓冲区:

1
2
3
4
5
typedef struct {
char *readWriteBuf;
unsigned int len;
unsigned int size;
} COAP_ReadWriteBuffer;

此外还定义有

1
2
3
4
#define COAP_VERSION 1
#define DEFAULT_TOK_LEN 0
#define MAX_TOK_LEN 8
#define HEADER_LEN 4
  • coap协议版本——1
  • 默认token长度——0
  • 最大token长度——8
  • 头部长度——4

以及数据包解析与打包过程之中所返回的执行结果的类型枚举:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
enum ErrorTypeEnum {
DISCOVERY_ERR_SUCCESS = 0,
DISCOVERY_ERR_HEADER_INVALID_SHORT = 1,
DISCOVERY_ERR_VER_INVALID = 2,
DISCOVERY_ERR_TOKEN_INVALID_SHORT = 3,
DISCOVERY_ERR_OPT_INVALID_SHORT_FOR_HEADER = 4,
DISCOVERY_ERR_OPT_INVALID_SHORT = 5,
DISCOVERY_ERR_OPT_OVERRUNS_PKT = 6,
DISCOVERY_ERR_OPT_INVALID_BIG = 7,
DISCOVERY_ERR_OPT_INVALID_LEN = 8,
DISCOVERY_ERR_BUF_INVALID_SMALL = 9,
DISCOVERY_ERR_NOT_SUPPORTED = 10,
DISCOVERY_ERR_OPT_INVALID_DELTA = 11,
DISCOVERY_ERR_PKT_EXCEED_MAX_PDU = 12,
DISCOVERY_ERR_TCP_TYPE_INVALID = 13,
DISCOVERY_ERR_UNKNOWN_MSG_TYPE = 14,
DISCOVERY_ERR_INVALID_PKT = 15,
DISCOVERY_ERR_INVALID_TOKEN_LEN = 16,
DISCOVERY_ERR_INVALID_ARGUMENT = 17,
DISCOVERY_ERR_TRANSPORT_NOT_UDP_OR_TCP = 18,
DISCOVERY_ERR_INVALID_EMPTY_MSG = 19,
DISCOVERY_ERR_SERVER_ERR = 20,
DISCOVERY_ERR_BAD_REQ = 21,
DISCOVERY_ERR_UNKNOWN_METHOD = 22,
DISCOVERY_ERR_BLOCK_NO_PAYLOAD = 23
};

以及最后定义的四个函数

1
2
3
4
int COAP_SoftBusDecode(COAP_Packet *pkt, const unsigned char *buf, unsigned int bufLen);
int COAP_SoftBusEncode(COAP_Packet *pkt, const COAP_PacketParam *param, const COAP_Buffer *token, const COAP_Buffer *payload, COAP_ReadWriteBuffer *buf);
void COAP_SoftBusInitMsgId(void);
int BuildSendPkt(const COAP_Packet *pkt, const char* remoteIp, const char *pktPayload, COAP_ReadWriteBuffer *sndPktBuff);

可以看出coap_adapter这一部分主要是负责了消息的打包和解析工作。

接着阅读源代码部分。

从文件所定义的几个函数来整体的看一下:

COAP_SoftBusDecode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
int COAP_SoftBusDecode(COAP_Packet *pkt, const unsigned char *buf, unsigned int bufLen)
{
int ret;
if (pkt == NULL || buf == NULL) {
return -1;
}

if (bufLen == 0) {
return -1;
}

if (pkt->protocol != COAP_UDP) {
return -1;
}

ret = COAP_ParseHeader(pkt, buf, bufLen);
if (ret != DISCOVERY_ERR_SUCCESS) {
return ret;
}

if (pkt->header.ver != COAP_VERSION) {
return DISCOVERY_ERR_VER_INVALID;
}

if (pkt->header.tokenLen > MAX_TOK_LEN) {
return DISCOVERY_ERR_INVALID_TOKEN_LEN;
}

if ((bufLen > HEADER_LEN) && (pkt->header.code == 0)) {
return DISCOVERY_ERR_INVALID_EMPTY_MSG;
}

if (pkt->header.tokenLen == 0) {
pkt->token.buffer = NULL;
pkt->token.len = 0;
} else if ((unsigned int)(pkt->header.tokenLen + HEADER_LEN) > bufLen) {
return DISCOVERY_ERR_TOKEN_INVALID_SHORT;
} else {
pkt->token.buffer = &buf[BUF_OFFSET_BYTE4];
pkt->token.len = pkt->header.tokenLen;
}

ret = COAP_ParseOptionsAndPayload(pkt, buf, bufLen);
if (ret != DISCOVERY_ERR_SUCCESS) {
return ret;
}

pkt->len = bufLen;
return DISCOVERY_ERR_SUCCESS;
}

处理对参数的检查外,我们可以看到有这么两个个关键的函数调用:

  • 16行的ret = COAP_ParseHeader(pkt, buf, bufLen);
  • 43行的ret = COAP_ParseOptionsAndPayload(pkt, buf, bufLen);

COAP_ParseHeader主要将 buf 中的数据通过按位与掩码和左移操作提取出数据包头部的信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
static int COAP_ParseHeader(COAP_Packet *pkt, const unsigned char *buf, unsigned int bufLen)
{
if (bufLen < HEADER_LEN) {
return DISCOVERY_ERR_HEADER_INVALID_SHORT;
}

pkt->header.ver = (((unsigned int)buf[0] >> COAP_SHIFT_BIT6) & 0x03);
pkt->header.type = ((((unsigned int)buf[0] & 0x30) >> COAP_SHIFT_BIT4) & 0x03);
pkt->header.tokenLen = (((unsigned int)buf[0] & 0x0F));
pkt->header.code = buf[1];
pkt->header.varSection.msgId = (unsigned short)((unsigned short)(buf[MSGID_HIGHT_BYTE] << COAP_SHIFT_BIT8)
| buf[BUF_OFFSET_BYTE3]);
return DISCOVERY_ERR_SUCCESS;
}

COAP_ParseOptionsAndPayload主要是通过COAP_ParseOption函数读取 Options 信息并通过pkt->payload.buffer = dataPos + 1;把数据包的数据负载部分提取出来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
static int COAP_ParseOptionsAndPayload(COAP_Packet *pkt, const unsigned char *buf,
unsigned int buflen)
{
unsigned char optionIndex = 0;
unsigned short delta = 0;
const unsigned char *dataPos = buf + HEADER_LEN + pkt->header.tokenLen;
const unsigned char *end = buf + buflen;

if (dataPos > end) {
return DISCOVERY_ERR_OPT_OVERRUNS_PKT;
}

while ((dataPos < end) && (*dataPos != 0xFF) && (optionIndex < COAP_MAX_OPTION)) {
int ret = COAP_ParseOption(&((pkt->options)[optionIndex]), &delta, &dataPos, end - dataPos);
if (ret != DISCOVERY_ERR_SUCCESS) {
return ret;
}
optionIndex++;
}

if ((dataPos < end) && (*dataPos != 0xFF) && (optionIndex >= COAP_MAX_OPTION)) {
return DISCOVERY_ERR_SERVER_ERR;
}
pkt->optionsNum = optionIndex;
if ((dataPos < end) && (*dataPos != 0xFF)) {
pkt->payload.buffer = NULL;
pkt->payload.len = 0;
return DISCOVERY_ERR_SUCCESS;
}

if (dataPos + 1 >= end) {
return DISCOVERY_ERR_INVALID_PKT;
}

pkt->payload.buffer = dataPos + 1;
pkt->payload.len = (unsigned int)(long)(end - (dataPos + 1));
return DISCOVERY_ERR_SUCCESS;
}

COAP_SoftBusEncode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
int COAP_SoftBusEncode(COAP_Packet *pkt, const COAP_PacketParam *param, const COAP_Buffer *token,
const COAP_Buffer *payload, COAP_ReadWriteBuffer *buf)
{
int ret;

if (pkt == NULL || param == NULL || buf == NULL || buf->readWriteBuf == NULL) {
return DISCOVERY_ERR_INVALID_EMPTY_MSG;
}

ret = COAP_CreateHeader(pkt, param, buf);
if (ret != DISCOVERY_ERR_SUCCESS) {
return ret;
}

if ((param->code == 0) && ((token != NULL) || (param->options != NULL) || (payload != NULL))) {
return DISCOVERY_ERR_INVALID_EMPTY_MSG;
}

ret = COAP_CreateBody(pkt, param, NULL, payload, buf);
if (ret != DISCOVERY_ERR_SUCCESS) {
return ret;
}

return DISCOVERY_ERR_SUCCESS;
}

同样的可以注意到处理参数检查外两个较为关键函数调用:

  • 第 10 行的ret = COAP_CreateHeader(pkt, param, buf);
  • 第 19 行的ret = COAP_CreateBody(pkt, param, NULL, payload, buf);

COAP_CreateHeader主要是检查所传入的参数,检查无误后将其复制到COAP_Packet中打包,组合生成数据包的头部。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
static int COAP_CreateHeader(COAP_Packet *pkt, const COAP_PacketParam *pktParam, COAP_ReadWriteBuffer *buf)
{
if (buf->len != 0) {
return DISCOVERY_ERR_INVALID_ARGUMENT;
}

if ((pktParam->protocol != COAP_UDP) && (pktParam->protocol != COAP_TCP)) {
return DISCOVERY_ERR_TRANSPORT_NOT_UDP_OR_TCP;
}
pkt->protocol = pktParam->protocol;

if (pktParam->type > COAP_TYPE_RESET) {
return DISCOVERY_ERR_UNKNOWN_MSG_TYPE;
}

if (buf->size < HEADER_LEN) {
return DISCOVERY_ERR_PKT_EXCEED_MAX_PDU;
}

pkt->header.type = (unsigned int)pktParam->type & 0x03;
pkt->header.ver = COAP_VERSION;
pkt->header.code = pktParam->code;

if (pkt->protocol == COAP_UDP) {
pkt->header.varSection.msgId = pktParam->msgId;
buf->readWriteBuf[0] = (char)(pkt->header.ver << COAP_SHIFT_BIT6);
buf->readWriteBuf[0] = (char)((unsigned char)buf->readWriteBuf[0] |
(unsigned char)(pkt->header.type << COAP_SHIFT_BIT4));
buf->readWriteBuf[1] = (char)pkt->header.code;
buf->readWriteBuf[BUF_OFFSET_BYTE2] = (char)((pkt->header.varSection.msgId & 0xFF00) >> COAP_SHIFT_BIT8);
buf->readWriteBuf[BUF_OFFSET_BYTE3] = (char)(pkt->header.varSection.msgId & 0x00FF);
} else {
return DISCOVERY_ERR_NOT_SUPPORTED;
}
pkt->len = buf->len = HEADER_LEN;
return DISCOVERY_ERR_SUCCESS;
}

另外可以看到在 24 行有一个小细节:

1
2
3
4
5
6
7
if (pkt->protocol == COAP_UDP) {
.
.
.
} else {
return DISCOVERY_ERR_NOT_SUPPORTED;
}

可以看出 CoAP 协议是基于传输层的 UDP 协议进行传输,代码层面并不支持打包 TCP 数据,因为打包 TCP 数据会返回DISCOVERY_ERR_NOT_SUPPORTED错误。

最后在COAP_CreateBody函数中主要进行了COAP_Packet数据包中TokenOptionData的填充。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
static int COAP_CreateBody(COAP_Packet *pkt, const COAP_PacketParam *param, const COAP_Buffer *token,
const COAP_Buffer *payload, COAP_ReadWriteBuffer *buf)
{
int i;
int ret;

if (token != NULL) {
ret = COAP_AddToken(pkt, token, buf);
if (ret != DISCOVERY_ERR_SUCCESS) {
return ret;
}
}

if (param->options != 0) {
if (param->optionsNum > COAP_MAX_OPTION) {
return DISCOVERY_ERR_SERVER_ERR;
}

for (i = 0; i < param->optionsNum; i++) {
ret = COAP_AddOption(pkt, &param->options[i], buf);
if (ret != DISCOVERY_ERR_SUCCESS) {
return ret;
}
}
}

if (payload != NULL) {
ret = COAP_AddData(pkt, payload, buf);
if (ret != DISCOVERY_ERR_SUCCESS) {
return ret;
}
}

return DISCOVERY_ERR_SUCCESS;
}

其中主要是这三个函数:

  • COAP_AddToken:填充令牌
  • COAP_AddOption:填充配置
  • COAP_AddData:填充负载数据

BuildSendPkt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
int BuildSendPkt(const COAP_Packet *pkt, const char* remoteIp, const char *pktPayload, COAP_ReadWriteBuffer *sndPktBuff)
{
COAP_Packet respPkt;
COAP_PacketParam respPktPara;
COAP_Option options[COAP_MAX_OPTION] = {0};

if (pkt == NULL || remoteIp == NULL || pktPayload == NULL || sndPktBuff == NULL) {
return DISCOVERY_ERR_BAD_REQ;
}

char *buf = sndPktBuff->readWriteBuf;
unsigned int len = sndPktBuff->size;

if (buf == NULL) {
return DISCOVERY_ERR_BAD_REQ;
}

(void)memset_s(&respPkt, sizeof(COAP_Packet), 0, sizeof(COAP_Packet));
(void)memset_s(&respPktPara, sizeof(COAP_PacketParam), 0, sizeof(COAP_PacketParam));
respPktPara.options = options;
respPktPara.options[respPktPara.optionsNum].num = DISCOVERY_MSG_URI_HOST;
respPktPara.options[respPktPara.optionsNum].optionBuf = (unsigned char *)remoteIp;
respPktPara.options[respPktPara.optionsNum].len = strlen(remoteIp);
respPktPara.optionsNum++;

respPktPara.options[respPktPara.optionsNum].num = DISCOVERY_MSG_URI_PATH;
respPktPara.options[respPktPara.optionsNum].optionBuf = (unsigned char *)"device_discover";
respPktPara.options[respPktPara.optionsNum].len = strlen("device_discover");
respPktPara.optionsNum++;

(void)memset_s(buf, COAP_MAX_PDU_SIZE, 0, COAP_MAX_PDU_SIZE);
COAP_ResponseInfo respInfo = {&respPkt, &respPktPara, NULL, 0};
respInfo.payload = (unsigned char *)pktPayload;
respInfo.payloadLen = strlen(pktPayload);
int ret = COAP_SoftBusBuildMessage(pkt, &respInfo, buf, &len);
if (ret != DISCOVERY_ERR_SUCCESS) {
return DISCOVERY_ERR_BAD_REQ;
}

if (len >= sndPktBuff->size) {
return DISCOVERY_ERR_BAD_REQ;
}
sndPktBuff->len = len;
return DISCOVERY_ERR_SUCCESS;
}

首先从第 3、4行的局部变量的命名(respPktrespPktPara)来看,这一个函数用于构建可发送的响应数据包以回应某个请求。

在经过对参数的检查后,在第 18~29 行给响应包填充了options数据。

最后可以看到在第 35 行调用了COAP_SoftBusBuildMessage函数进行响应数据的构建。

COAP_SoftBusBuildMessage函数中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
int COAP_SoftBusBuildMessage(const COAP_Packet *req, const COAP_ResponseInfo *resqInfo, char *buf, unsigned int *len)
{
int ret;
COAP_ReadWriteBuffer outBuf;
COAP_Buffer inPayload;
COAP_Buffer inToken;

if (resqInfo == NULL || resqInfo->pkt == NULL
|| resqInfo->param == NULL || buf == NULL
|| len == NULL) {
return DISCOVERY_ERR_BAD_REQ;
}

if (*len == 0) {
return DISCOVERY_ERR_BAD_REQ;
}

(void)memset_s(&outBuf, sizeof(COAP_ReadWriteBuffer), 0, sizeof(COAP_ReadWriteBuffer));
(void)memset_s(&inPayload, sizeof(COAP_Buffer), 0, sizeof(COAP_Buffer));
(void)memset_s(&inToken, sizeof(COAP_Buffer), 0, sizeof(COAP_Buffer));

outBuf.readWriteBuf = buf;
outBuf.size = *len;
inPayload.buffer = resqInfo->payload;
inPayload.len = resqInfo->payloadLen;
if (resqInfo->payloadLen >= *len) {
return DISCOVERY_ERR_BAD_REQ;
}

ret = COAP_BuildResponseParam(req, resqInfo, &inToken);
if (ret != DISCOVERY_ERR_SUCCESS) {
return ret;
}

if ((resqInfo->payload == NULL) || (resqInfo->payloadLen == 0)) {
ret = COAP_SoftBusEncode(resqInfo->pkt, resqInfo->param, &inToken, NULL, &outBuf);
} else {
ret = COAP_SoftBusEncode(resqInfo->pkt, resqInfo->param, &inToken, &inPayload, &outBuf);
}

if (ret != DISCOVERY_ERR_SUCCESS) {
return DISCOVERY_ERR_BAD_REQ;
}

*len = outBuf.len;
return ret;
}

也先是对参数进行检查,紧接着在第 30 行(ret = COAP_BuildResponseParam(req, resqInfo, &inToken);)调用函数,完成了响应包的参数构建。

最后根据是否携带有效负载数据,分别在 36、38 行调用COAP_SoftBusEncode函数对数据包进行编码。