欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

LWIP之UDP协议

程序员文章站 2024-02-22 18:27:40
...

IP协议提供了在各个主机之间传送数据报的功能,但是数据的最终目的地是主机上的特定应用程序。传输层协议就承担了这样的责任,典型的传输层协议有UDP和TCP两种。许多著名的上层应用都是基于UDP实现的,比如DNS、DHCP、IGMP、SNMP等。

 

UDP协议称为用户数据包协议,是一种无连接、不可靠的传输协议。UDP协议只是简单地完成应用程序到应用程序的交互,并不提供流量控制机制、复杂的差错控制方法、确认机制。虽然UDP可靠性非常差,但是UDP适用于那些轻微数据差错不敏感的应用,比如视频传输、网络电话等。

 

每台主机都包含称为协议端口的抽象目的点,端口号范围为0~65535,进程绑定到端口号上。这样数据包只要递交到相应的端口即可。

 

报文格式

LWIP之UDP协议

源端口号和目的端口号不用多讲

总长度:首部长度+数据区长度

校验和:UDP校验和比较特殊,包含伪首部、首部和数据区。全0表示不使用校验和,校验和为0时用0xFFFF代替。

LWIP之UDP协议

 

先看一下UDP控制块

#define IP_PCB                             \
  ip_addr_t local_ip;                      \
  ip_addr_t remote_ip;                     \
  u8_t netif_idx;                          \
  u8_t so_options;                         \
  u8_t tos;                                \
  u8_t ttl                                 \
  IP_PCB_NETIFHINT

/* UDP控制块 */
struct udp_pcb 
{
  IP_PCB;			        //IP_PCB相关字段
  struct udp_pcb *next;		        //下一个UDP控制块指针
  u8_t flags;				//控制块状态
  u16_t local_port, remote_port;	//本地端口和远程端口
  udp_recv_fn recv;			//接收回调函数
  void *recv_arg;			//接收回调函数参数
};

UDP控制块被组织成一张表

LWIP之UDP协议

 

下面分析UDP创建和删除的一些API

/* 创建UDP控制块 */
struct udp_pcb *udp_new(void)
{
  struct udp_pcb *pcb;

  /* 为UDP控制块申请内存 */
  pcb = (struct udp_pcb *)memp_malloc(MEMP_UDP_PCB);
  if (pcb != NULL) 
  {
    /* 清空UDP控制块 */
    memset(pcb, 0, sizeof(struct udp_pcb));
    /* TTL值 */
    pcb->ttl = UDP_TTL;
  }

  return pcb;
}
/* 绑定端口号 */
err_t udp_bind(struct udp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port)
{
  struct udp_pcb *ipcb;
  u8_t rebind;

  /* 没指定IP */
  if (ipaddr == NULL) {
    ipaddr = IP4_ADDR_ANY;
  }

  /* 检查UDP控制块是否已经绑定 */
  rebind = 0;
  for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) {
    if (pcb == ipcb) {
      rebind = 1;
      break;
    }
  }

  /* 未指定端口 */
  if (port == 0) {
  	/* 自动分配端口 */
    port = udp_new_port();
    if (port == 0) {
      return ERR_USE;
    }
  } 
  /* 指定端口 */
  else 
  {
    /* 遍历所有UDP控制块,检查端口是否已经使用 */
    for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) 
    {
      if (pcb != ipcb) {
        if ((ipcb->local_port == port) && (ip_addr_cmp(&ipcb->local_ip, ipaddr) || 
            ip_addr_isany(ipaddr) || ip_addr_isany(&ipcb->local_ip))) {
          return ERR_USE;
        }
      }
    }
  }

  /* 设置本地IP和端口号 */
  ip_addr_set_ipaddr(&pcb->local_ip, ipaddr);
  pcb->local_port = port;
  
  /* 将UDP控制块插入链表 */
  if (rebind == 0) {
    pcb->next = udp_pcbs;
    udp_pcbs = pcb;
  }

  return ERR_OK;
}
/* 绑定远程端口 */
err_t udp_connect(struct udp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port)
{
  struct udp_pcb *ipcb;

  /* 连接之前必须完成绑定 */
  if (pcb->local_port == 0) {
    err_t err = udp_bind(pcb, &pcb->local_ip, pcb->local_port);
    if (err != ERR_OK) {
      return err;
    }
  }

  /* 设置远程IP和端口号 */
  ip_addr_set_ipaddr(&pcb->remote_ip, ipaddr);
  pcb->remote_port = port;
  pcb->flags |= UDP_FLAGS_CONNECTED;

  /* UDP控制块必须插入链表 */
  for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) {
    if (pcb == ipcb) {
      return ERR_OK;
    }
  }
  pcb->next = udp_pcbs;
  udp_pcbs = pcb;
  return ERR_OK;
}
/* 断开远程端口 */
void udp_disconnect(struct udp_pcb *pcb)
{
  /* 清空远程IP和端口 */
  ip_addr_set_any(IP_IS_V6_VAL(pcb->remote_ip), &pcb->remote_ip);
  pcb->remote_port = 0;
  pcb->netif_idx = NETIF_NO_INDEX;
  udp_clear_flags(pcb, UDP_FLAGS_CONNECTED);
}
/* 注册接收回调函数 */
void udp_recv(struct udp_pcb *pcb, udp_recv_fn recv, void *recv_arg)
{
  pcb->recv = recv;
  pcb->recv_arg = recv_arg;
}
/* 删除UDP控制块 */
void udp_remove(struct udp_pcb *pcb)
{
  struct udp_pcb *pcb2;

  /* 从链表中移除 */
  if (udp_pcbs == pcb) {
    udp_pcbs = udp_pcbs->next;
  } 
  else 
  {
    for (pcb2 = udp_pcbs; pcb2 != NULL; pcb2 = pcb2->next) 
    {
      if (pcb2->next != NULL && pcb2->next == pcb) 
      {
        pcb2->next = pcb->next;
        break;
      }
    }
  }

  /* 释放内存 */
  memp_free(MEMP_UDP_PCB, pcb);
}

接下来看一下UDP发送API

/* UDP发送 */
err_t udp_send(struct udp_pcb *pcb, struct pbuf *p)
{
  if (IP_IS_ANY_TYPE_VAL(pcb->remote_ip)) {
    return ERR_VAL;
  }

  return udp_sendto(pcb, p, &pcb->remote_ip, pcb->remote_port);
}

err_t udp_sendto(struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip, u16_t dst_port)
{
  struct netif *netif;

  if (!IP_ADDR_PCB_VERSION_MATCH(pcb, dst_ip)) {
    return ERR_VAL;
  }

  /* 查找合适的网络接口 */
  if (pcb->netif_idx != NETIF_NO_INDEX) {
    netif = netif_get_by_index(pcb->netif_idx);
  } else {
    netif = ip_route(&pcb->local_ip, dst_ip);
  }
  if (netif == NULL) {
    return ERR_RTE;
  }

  /* 发送数据包到指定网络接口 */
  return udp_sendto_if(pcb, p, dst_ip, dst_port, netif);
}

err_t udp_sendto_if(struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip, u16_t dst_port, 
                    struct netif *netif)
{
  const ip_addr_t *src_ip;

  if (!IP_ADDR_PCB_VERSION_MATCH(pcb, dst_ip)) {
    return ERR_VAL;
  }

  /* 取出源IP用于伪首部校验和 */
  if (ip4_addr_isany(ip_2_ip4(&pcb->local_ip)) ||
      ip4_addr_ismulticast(ip_2_ip4(&pcb->local_ip))) {
    src_ip = netif_ip_addr4(netif);
  } else {
    if (!ip4_addr_cmp(ip_2_ip4(&(pcb->local_ip)), netif_ip4_addr(netif))) {
      return ERR_RTE;
    }
    src_ip = &pcb->local_ip;
  }

  /* 发送数据包 */
  return udp_sendto_if_src(pcb, p, dst_ip, dst_port, netif, src_ip);
}

err_t udp_sendto_if_src(struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip, u16_t dst_port, 
                        struct netif *netif, const ip_addr_t *src_ip)
{
  struct udp_hdr *udphdr;
  err_t err;
  struct pbuf *q;
  u8_t ip_proto;
  u8_t ttl;

  if (!IP_ADDR_PCB_VERSION_MATCH(pcb, src_ip) ||
      !IP_ADDR_PCB_VERSION_MATCH(pcb, dst_ip)) {
    return ERR_VAL;
  }

  /* 发送数据包之前必须先绑定本地端口 */
  if (pcb->local_port == 0) {
    err = udp_bind(pcb, &pcb->local_ip, pcb->local_port);
    if (err != ERR_OK) {
      return err;
    }
  }

  /* UDP数据大小 */
  if ((u16_t)(p->tot_len + UDP_HLEN) < p->tot_len) {
    return ERR_MEM;
  }
  /* 添加UDP头部 */
  if (pbuf_add_header(p, UDP_HLEN)) {
    q = pbuf_alloc(PBUF_IP, UDP_HLEN, PBUF_RAM);
    if (q == NULL) {
      return ERR_MEM;
    }
    if (p->tot_len != 0) {
      pbuf_chain(q, p);
    }
  } else {
    q = p;
  }

  /* 填写UDP头部 */
  udphdr = (struct udp_hdr *)q->payload;
  udphdr->src = lwip_htons(pcb->local_port);
  udphdr->dest = lwip_htons(dst_port);
  udphdr->chksum = 0x0000;
  udphdr->len = lwip_htons(q->tot_len);
  /* 计算校验和 */
  IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_UDP) {
    if (IP_IS_V6(dst_ip) || (pcb->flags & UDP_FLAGS_NOCHKSUM) == 0) {
      u16_t udpchksum;
      {
        udpchksum = ip_chksum_pseudo(q, IP_PROTO_UDP, q->tot_len, src_ip, dst_ip);
      }

      if (udpchksum == 0x0000) {
        udpchksum = 0xffff;
      }
      udphdr->chksum = udpchksum;
    }
  }
  ip_proto = IP_PROTO_UDP;
  ttl = pcb->ttl;

  /* 发送数据包 */
  err = ip_output_if_src(q, src_ip, dst_ip, ttl, pcb->tos, ip_proto, netif);

  /* 释放pbuf */
  if (q != p) {
    pbuf_free(q);
    q = NULL;
  }

  return err;
}

最后看一下接口函数

/* UDP输入 */
void udp_input(struct pbuf *p, struct netif *inp)
{
  struct udp_hdr *udphdr;
  struct udp_pcb *pcb, *prev;
  struct udp_pcb *uncon_pcb;
  u16_t src, dest;
  u8_t broadcast;
  u8_t for_us = 0;

  /* 校验数据包长度 */
  if (p->len < UDP_HLEN) {
    pbuf_free(p);
    goto end;
  }

  /* 取出UDP头部 */
  udphdr = (struct udp_hdr *)p->payload;

  /* 目的地址是否是广播地址 */
  broadcast = ip_addr_isbroadcast(ip_current_dest_addr(), ip_current_netif());

  /* 取出源端口和目的端口 */
  src = lwip_ntohs(udphdr->src);
  dest = lwip_ntohs(udphdr->dest);

  pcb = NULL;
  prev = NULL;
  uncon_pcb = NULL;
  
  /* 遍历所有UDP控制块 */
  for (pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) {
    /* 目的端口和地址都匹配 */
    if ((pcb->local_port == dest) &&
        (udp_input_local_match(pcb, inp, broadcast) != 0)) 
    {
      /* 记录第一个未连接UDP控制块 */
      if ((pcb->flags & UDP_FLAGS_CONNECTED) == 0) {
        if (uncon_pcb == NULL) {
          uncon_pcb = pcb;
        } else if (broadcast && ip4_current_dest_addr()->addr == IPADDR_BROADCAST) {
          if (!IP_IS_V4_VAL(uncon_pcb->local_ip) || !ip4_addr_cmp(ip_2_ip4(&uncon_pcb->local_ip), netif_ip4_addr(inp))) {
            if (IP_IS_V4_VAL(pcb->local_ip) && ip4_addr_cmp(ip_2_ip4(&pcb->local_ip), netif_ip4_addr(inp))) {
              uncon_pcb = pcb;
            }
          }
        }
      }

      /* 匹配源端口和IP地址 */
      if ((pcb->remote_port == src) &&
          (ip_addr_isany_val(pcb->remote_ip) ||
           ip_addr_cmp(&pcb->remote_ip, ip_current_src_addr()))) {
        /* 将该控制块移到链表首部 */
        if (prev != NULL) 
        {
          prev->next = pcb->next;
          pcb->next = udp_pcbs;
          udp_pcbs = pcb;
        } else {

        }
        break;
      }
    }

    prev = pcb;
  }
  /* 未完全匹配到目的端口地址、源端口地址、已连接的PCB */
  if (pcb == NULL) 
  {
    /* 将第一个目的地址端口都匹配的未连接PCB */
    pcb = uncon_pcb;
  }

  /* 匹配到则认为该数据包是发给自己的 */
  if (pcb != NULL) {
    for_us = 1;
  } else {
    if (!ip_current_is_v6()) {
      for_us = ip4_addr_cmp(netif_ip4_addr(inp), ip4_current_dest_addr());
    }
  }

  /* 发给自己的数据包,则进行处理 */
  if (for_us) {
	/* 进行校验 */
    IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_CHECK_UDP) {
      if (udphdr->chksum != 0) {
        if (ip_chksum_pseudo(p, IP_PROTO_UDP, p->tot_len,
                             ip_current_src_addr(),
                             ip_current_dest_addr()) != 0) {
          goto chkerr;
        }
      }
    }

	/* 移除头部 */
    if (pbuf_remove_header(p, UDP_HLEN)) {
      pbuf_free(p);
      goto end;
    }

    /* 调用接收回调函数 */
    if (pcb != NULL) {
      if (pcb->recv != NULL) {
        pcb->recv(pcb->recv_arg, pcb, p, ip_current_src_addr(), src);
      } else {
        pbuf_free(p);
        goto end;
      }
    } 
    /* 目的地不可达ICMP报文 */
    else 
    {
      if (!broadcast && !ip_addr_ismulticast(ip_current_dest_addr())) {
          pbuf_header_force(p, (s16_t)(ip_current_header_tot_len() + UDP_HLEN));
        icmp_port_unreach(ip_current_is_v6(), p);
      }

      pbuf_free(p);
    }
  }
  /* 不是发给自己的直接删除 */
  else {
    pbuf_free(p);
  }

end:
  return;

chkerr:
  pbuf_free(p);
}

 

相关标签: LwIP