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和端口号的数据,其他的都会丢弃处理。
在这里插入图片描述

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值