lwip-udp rawapi 使用詳解

lwip-udp rawapi 使用詳解

udp簡介

udp是無連接的傳輸層協議,沒有客戶端和服務器的概念。

rawapi使用詳解

  1. udp_new,創建一個udp的控制塊,記錄的關鍵信息有,本地端口號,遠程端口號,標誌位flags,回調函數指針,本地ip,遠程ip
  2. udp_bind,給udp的pcb控制塊,設置本地ip, 本地端口號,這兩個值非常重要,後面會說
err_t
udp_bind(struct udp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port)
{
  struct udp_pcb *ipcb;
  u8_t rebind;

#if LWIP_IPV4
  /* Don't propagate NULL pointer (IPv4 ANY) to subsequent functions */
  if (ipaddr == NULL) {
    ipaddr = IP4_ADDR_ANY;
  }
#endif /* LWIP_IPV4 */

  /* still need to check for ipaddr == NULL in IPv6 only case */
  if ((pcb == NULL) || (ipaddr == NULL)) {
    return ERR_VAL;
  }

  LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_bind(ipaddr = "));
  ip_addr_debug_print(UDP_DEBUG | LWIP_DBG_TRACE, ipaddr);
  LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, (", port = %"U16_F")\n", port));

  rebind = 0;
  /* Check for double bind and rebind of the same pcb */
  for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) {//檢查激活的鏈表,選擇是否有重複綁定
    /* is this UDP PCB already on active list? */
    if (pcb == ipcb) {
      rebind = 1;//已經綁定過了,置標誌位
      break;
    }
  }

  /* no port specified? */
  if (port == 0) {
    port = udp_new_port(); //如果傳入的端口號爲0,則系統自己生成一個端口號
    if (port == 0) {
      /* no more ports available in local range */
      LWIP_DEBUGF(UDP_DEBUG, ("udp_bind: out of free UDP ports\n"));//系統自動生成端口號失敗
      return ERR_USE;
    }
  } else {
    for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) {//遍歷鏈表,檢查端口號是否有重複綁定
      if (pcb != ipcb) {
      /* By default, we don't allow to bind to a port that any other udp
         PCB is already bound to, unless *all* PCBs with that port have tha
         REUSEADDR flag set. */
#if SO_REUSE
        if (!ip_get_option(pcb, SOF_REUSEADDR) ||
            !ip_get_option(ipcb, SOF_REUSEADDR))
#endif /* SO_REUSE */
        {
          /* port matches that of PCB in list and REUSEADDR not set -> reject */
          if ((ipcb->local_port == port) &&
              /* IP address matches? */
              ip_addr_cmp(&ipcb->local_ip, ipaddr)) {//相同端口號,綁定到相同的ip報錯
            /* other PCB already binds to this local IP and port */
            LWIP_DEBUGF(UDP_DEBUG,
                        ("udp_bind: local port %"U16_F" already bound by another pcb\n", port));
            return ERR_USE;
          }
        }
      }
    }
  }

  ip_addr_set_ipaddr(&pcb->local_ip, ipaddr);//設置本地ip

  pcb->local_port = port; //設置本地端口號
  mib2_udp_bind(pcb);
  /* pcb not active yet? */
  if (rebind == 0) {
    /* place the PCB on the active list if not already there */
    pcb->next = udp_pcbs;//初次綁定,需要將綁定的pcb記錄到鏈表
    udp_pcbs = pcb;
  }
  LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("udp_bind: bound to "));
  ip_addr_debug_print(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, &pcb->local_ip);
  LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, (", port %"U16_F")\n", pcb->local_port));
  return ERR_OK;
}
  1. udp_recv,註冊回調函數
  2. udp_connect,給udp的pcb控制塊,設置遠程端口號,遠程ip,並且給連接標誌位置1
err_t
udp_connect(struct udp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port)
{
  struct udp_pcb *ipcb;

  if ((pcb == NULL) || (ipaddr == NULL)) {
    return ERR_VAL;
  }

  if (pcb->local_port == 0) {
    err_t err = udp_bind(pcb, &pcb->local_ip, pcb->local_port);//本地端口號爲0,需要自動分配端口號
    if (err != ERR_OK) {
      return err;
    }
  }

  ip_addr_set_ipaddr(&pcb->remote_ip, ipaddr);//設置遠程ip
  pcb->remote_port = port; //設置遠程端口號
  pcb->flags |= UDP_FLAGS_CONNECTED;//連接標誌位置位

  LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("udp_connect: connected to "));
  ip_addr_debug_print(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
                      &pcb->remote_ip);
  LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, (", port %"U16_F")\n", pcb->remote_port));

  /* Insert UDP PCB into the list of active UDP PCBs. */
  for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) {
    if (pcb == ipcb) {
      /* already on the list, just return */
      return ERR_OK;
    }
  }
  /* PCB not yet on the list, add PCB now */
  pcb->next = udp_pcbs;
  udp_pcbs = pcb;
  return ERR_OK;
}
  1. udp_sendto,指定ip和端口號,發送數據
  2. udp_send,使用udp_connect連接的端口號和ip發送數據

udp_input過濾數據原理

下面的這段代碼是最關鍵的,目的就是根據,本地ip,本地端口號,遠程ip,遠程端口號,來找到對應的pcb,最終調用回調函數處理數據。

void
udp_input(struct pbuf *p, struct netif *inp)
{
...//省略部分代碼
  /* Iterate through the UDP pcb list for a matching pcb.
   * 'Perfect match' pcbs (connected to the remote port & ip address) are
   * preferred. If no perfect match is found, the first unconnected pcb that
   * matches the local port and ip address gets the datagram. */
  for (pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) {
    /* print the PCB local and remote address */
    LWIP_DEBUGF(UDP_DEBUG, ("pcb ("));
    ip_addr_debug_print(UDP_DEBUG, &pcb->local_ip);
    LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F") <-- (", pcb->local_port));
    ip_addr_debug_print(UDP_DEBUG, &pcb->remote_ip);
    LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F")\n", pcb->remote_port));

    /* compare PCB local addr+port to UDP destination addr+port */
    if ((pcb->local_port == dest) &&
        (udp_input_local_match(pcb, inp, broadcast) != 0)) {
      if (((pcb->flags & UDP_FLAGS_CONNECTED) == 0) &&
          ((uncon_pcb == NULL)
#if SO_REUSE
          /* prefer specific IPs over cath-all */
          || !ip_addr_isany(&pcb->local_ip)
#endif /* SO_REUSE */
          )) {
        /* the first unconnected matching PCB */
        uncon_pcb = pcb;
      }

      /* compare PCB remote addr+port to UDP source addr+port */
      if ((pcb->remote_port == src) &&
          (ip_addr_isany_val(pcb->remote_ip) ||
          ip_addr_cmp(&pcb->remote_ip, ip_current_src_addr()))) {
        /* the first fully matching PCB */
        if (prev != NULL) {
          /* move the pcb to the front of udp_pcbs so that is
             found faster next time */
          prev->next = pcb->next;
          pcb->next = udp_pcbs;
          udp_pcbs = pcb;
        } else {
          UDP_STATS_INC(udp.cachehit);
        }
        break;
      }
    }
    ...//省略部分代碼

在這裏插入圖片描述

udp rawapi使用詳解

//回調函數
void recv_callback_tftp(void *arg, struct udp_pcb *upcb, struct pbuf *pkt_buf,
                        const ip_addr_t *addr, u16_t port)
{
    memcpy((char *)&g_UdpBuff,(pkt_buf->payload),pkt_buf->len);
    udp_sendto(upcb,pkt_buf,addr,port);
    pbuf_free(pkt_buf);
}
    err_t err = 0;
    struct udp_pcb *UDPpcb = NULL;
    //1. 創建pcb
    UDPpcb = udp_new();
    if (NULL == UDPpcb)
    {
        return 0;
    }
    //2. 註冊回調函數
    udp_recv(UDPpcb, recv_callback_tftp, NULL);
   
   //3. pcb本地ip, 端口號賦值,綁定
     err = udp_bind(UDPpcb, IP_ADDR_ANY, 8849);
    if (err != ERR_OK)
    {    /* Unable to bind to port  */
        return 0;
    }
    
  //4. pcb遠程ip,端口號賦值, 連接
    struct ip4_addr destAddr;
    IP4_ADDR(&destAddr,10,8,113,29);
    err = udp_connect(UDPpcb,&destAddr,8848);
    if (err != ERR_OK)
    {    /* Unable to bind to port  */
        return 0;
    }   
//5.不指定ip端口號 發送數據
            pkt_buf=pbuf_alloc(PBUF_TRANSPORT, strlen(udp_demo_sendbuf),PBUF_RAM);    
            pbuf_take(pkt_buf,(char*)udp_demo_sendbuf, strlen(udp_demo_sendbuf));    
            udp_send(UDPpcb,pkt_buf);
            pbuf_free(pkt_buf);
//6. 指定ip端口號發送數據            
            pkt_buf=pbuf_alloc(PBUF_TRANSPORT, strlen(udp_demo_sendbuf1),PBUF_RAM);    
            pbuf_take(pkt_buf,(char*)udp_demo_sendbuf1, strlen(udp_demo_sendbuf1));    
            udp_sendto(UDPpcb,pkt_buf,&destAddr,8850);
            pbuf_free(pkt_buf);

以上配置1可以收到數據
以上配置1可以收到數據
以上配置2可以收到數據
以上配置2可以收到數據

去掉第4步是否可以發送數據,第5步發送數據,是否可以成功

不可以,因爲,沒有給遠程ip端口號賦值

以下配置發送數據,是否可以收到回覆

在這裏插入圖片描述
不能,根據udp_input的邏輯,遠程ip和端口號,第一步可以通過,但是設備udppcb的遠程ip和端口號,是不匹配的,所以這一幀數據將會被丟棄

如何纔可以收到回覆,刪掉第4步,這樣的話,就是隻會判斷遠程的端口號是否正確,而不會關注,是那個ip發過來的數據。這種方式十分好用,通常是作爲服務器時,任何客戶端ip通過該端口發過來的數據,都會得到處理

如果保留了第4步的話,就只有只會處理連接的ip和端口號的數據,其他的都會丟棄處理。
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章