本篇博客主要介绍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
数据包中Token
、Option
、Data
的填充。
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, ¶m->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行的局部变量的命名(respPkt
、respPktPara
)来看,这一个函数用于构建可发送的响应数据包以回应某个请求。
在经过对参数的检查后,在第 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
函数对数据包进行编码。