libcoap的使用和CoAP協議分析一

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_readcoap_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);
  }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章