libcoap是一個coap協議的lib庫,使用方便,搭建coap快捷方便!
libcoap提供了example,以下從example中分析coap協議的數據封裝!
libcoap安裝編譯之後,在example下生成 coap-server和coap-client 可執行文件。
coap-server在命令行的usage信息
coap-client在命令行下的usage信息:
分析server.c文件,
main函數中,先進行初始化,然後使用select進行輪詢
UDP的監聽服務socket構建好之後,使用函數coap_read
來接受數據
使用coap_dispatch
來處理接收到的數據併發送反饋信息
重點分析coap_read
和coap_dispatch
在coap_read
中調用coap_pdu_parse
來對接收到的數據進行coap協議解析:
if (!coap_pdu_parse((unsigned char *)buf, bytes_read, node->pdu)) {
warn("discard malformed PDU");
goto error;
}
coap_pdu_parse
函數如下:
int
coap_pdu_parse(unsigned char *data, size_t length, coap_pdu_t *pdu) {
coap_opt_t *opt;
assert(data);
assert(pdu);
if (pdu->max_size < length) {
debug("insufficient space to store parsed PDU\n");
return 0;
}
if (length < sizeof(coap_hdr_t)) {
debug("discarded invalid PDU\n");
}
/*解析coap協議頭部*/
pdu->hdr->version = data[0] >> 6;
pdu->hdr->type = (data[0] >> 4) & 0x03;
pdu->hdr->token_length = data[0] & 0x0f;
pdu->hdr->code = data[1];
pdu->data = NULL;
/* sanity checks *//*檢查code*/
if (pdu->hdr->code == 0) {
if (length != sizeof(coap_hdr_t) || pdu->hdr->token_length) {
debug("coap_pdu_parse: empty message is not empty\n");
goto discard;
}
}
if (length < sizeof(coap_hdr_t) + pdu->hdr->token_length
|| pdu->hdr->token_length > 8) {
debug("coap_pdu_parse: invalid Token\n");
goto discard;
}
/* Copy message id in network byte order, so we can easily write the
* response back to the network. */
/*獲取memssage id值*/
memcpy(&pdu->hdr->id, data + 2, 2);
/* append data (including the Token) to pdu structure */
/* 獲取token的值*/
memcpy(pdu->hdr + 1, data + sizeof(coap_hdr_t), length - sizeof(coap_hdr_t));
pdu->length = length;
/* Finally calculate beginning of data block and thereby check integrity
* of the PDU structure. */
/* skip header + token */
/* 獲取option選項*/
length -= (pdu->hdr->token_length + sizeof(coap_hdr_t));
opt = (unsigned char *)(pdu->hdr + 1) + pdu->hdr->token_length;
/*解析option,知道0xFF時停止*/
while (length && *opt != COAP_PAYLOAD_START) {
if (!next_option_safe(&opt, (size_t *)&length)) {
debug("coap_pdu_parse: drop\n");
goto discard;
}
}
/* end of packet or start marker */
/* 最後獲取payload*/
if (length) {
assert(*opt == COAP_PAYLOAD_START);
opt++; length--;
if (!length) {
debug("coap_pdu_parse: message ending in payload start marker\n");
goto discard;
}
debug("set data to %p (pdu ends at %p)\n", (unsigned char *)opt,
(unsigned char *)pdu->hdr + pdu->length);
pdu->data = (unsigned char *)opt;
}
return 1;
discard:
return 0;
}
coap_dispatch分析接收信息並作出反饋
void
coap_dispatch( coap_context_t *context ) {
coap_queue_t *rcvd = NULL, *sent = NULL;
coap_pdu_t *response;
coap_opt_filter_t opt_filter;
if (!context)
return;
memset(opt_filter, 0, sizeof(coap_opt_filter_t));
while ( context->recvqueue ) {
rcvd = context->recvqueue;
/* remove node from recvqueue */
context->recvqueue = context->recvqueue->next;
rcvd->next = NULL;
/* 檢查coap協議的版本*/
if ( rcvd->pdu->hdr->version != COAP_DEFAULT_VERSION ) {
debug("dropped packet with unknown version %u\n", rcvd->pdu->hdr->version);
goto cleanup;
}
/* 檢測type信息類型,4中類型,並作出反饋*/
switch ( rcvd->pdu->hdr->type ) {
case COAP_MESSAGE_ACK:
/* find transaction in sendqueue to stop retransmission */
coap_remove_from_queue(&context->sendqueue, rcvd->id, &sent);
if (rcvd->pdu->hdr->code == 0)
goto cleanup;
/* FIXME: if sent code was >= 64 the message might have been a
* notification. Then, we must flag the observer to be alive
* by setting obs->fail_cnt = 0. */
if (sent && COAP_RESPONSE_CLASS(sent->pdu->hdr->code) == 2) {
const str token =
{ sent->pdu->hdr->token_length, sent->pdu->hdr->token };
coap_touch_observer(context, &sent->remote, &token);
}
break;
case COAP_MESSAGE_RST :
/* We have sent something the receiver disliked, so we remove
* not only the transaction but also the subscriptions we might
* have. */
coap_log(LOG_ALERT, "got RST for message %u\n", ntohs(rcvd->pdu->hdr->id));
/* find transaction in sendqueue to stop retransmission */
coap_remove_from_queue(&context->sendqueue, rcvd->id, &sent);
if (sent)
coap_cancel(context, sent);
goto cleanup;
case COAP_MESSAGE_NON : /* check for unknown critical options */
if (coap_option_check_critical(context, rcvd->pdu, opt_filter) == 0)
goto cleanup;
break;
case COAP_MESSAGE_CON : /* check for unknown critical options */
if (coap_option_check_critical(context, rcvd->pdu, opt_filter) == 0) {
/* FIXME: send response only if we have received a request. Otherwise,
* send RST. */
response =
coap_new_error_response(rcvd->pdu, COAP_RESPONSE_CODE(402), opt_filter);
if (!response)
warn("coap_dispatch: cannot create error reponse\n");
else {
if (coap_send(context, &rcvd->remote, response)
== COAP_INVALID_TID) {
warn("coap_dispatch: error sending reponse\n");
}
coap_delete_pdu(response);
}
goto cleanup;
}
break;
}
/* Pass message to upper layer if a specific handler was
* registered for a request that should be handled locally. */
/* 這裏通過解析option 執行對應的option操作,操作函數在init_resources中指定*/
if (handle_locally(context, rcvd)) {
if (COAP_MESSAGE_IS_REQUEST(rcvd->pdu->hdr))
handle_request(context, rcvd);
else if (COAP_MESSAGE_IS_RESPONSE(rcvd->pdu->hdr))
handle_response(context, sent, rcvd);
else {
debug("dropped message with invalid code (%d.%02d)\n",
COAP_RESPONSE_CLASS(rcvd->pdu->hdr->code),
rcvd->pdu->hdr->code & 0x1f);
coap_send_message_type(context, &rcvd->remote, rcvd->pdu,
COAP_MESSAGE_RST);
}
}
cleanup:
coap_delete_node(sent);
coap_delete_node(rcvd);
}
}