分享一个UDP通信中,远程主机随机变化端口号的开发经验

2019-07-20 08:59发布

手上开发一个项目,做一个本机设备和网络主机设备UDP通信。主机发送过一次数据包之后,本机每隔256ms循环给主机发送数据包。
主机每次启动的端口号是随机的。那么udp层在判断端口匹配的时候不能丢弃掉目标端口和Ip不匹配的包。
     if ((uncon_pcb == NULL) &&
              ((pcb->flags & UDP_FLAGS_CONNECTED) == 0)) {
            /* the first unconnected matching PCB */
            uncon_pcb = pcb;
由于这段代码,那么在应用程序初始化的时候只能进行udp_bind,而不能进行udp_connect,以免 flag的值导致不能进入上述语句中if的条件。
我想了个办法,在udp的rev回调函数中,进行udp_connect
void udp_recv(void *arg,struct udp_pcb *upcb,struct pbuf *p,struct ip_addr *addr,u16_t port)
{
   ...
   udp_connect(upcb,addr,port);
   ...
}
下载进行测试,可以使用随机端口进行通信。但是出现了新的问题:
如果网络主机运行中故障重启,那么本机不重新启动的话无法连接,因为执行过udp_connect后,pcb->flag被标记为已经连接。
为了解决这个问题,我new了2个(或者n个)udp_pcb,pcb1和pcb2均绑定同一个port和ip,当udp收到数据后,从pcb池里找到其中一个,进入rev回调,检查udp找到的是哪一个,如果是pcb1,则udp_connect(pcb1),并且disconnect(pcb2),如果是pcb2,则相反执行。
void udp_recv(void *arg,struct udp_pcb *upcb,struct pbuf *p,struct ip_addr *addr,u16_t port)
{
       udp_pcb *pcb_ptr;
       pcb_ptr=find_pcb(upcb);
      
       udp_disconnect_all();
       udp_connect(pcb_ptr);
       .....
}
结果编译后,测试失败了。
仿真找了一下原因发现lwip默认不允许2个pcb绑定同一端口号和ip:
#if SO_REUSE
    else if (!ip_get_option(pcb, SOF_REUSEADDR) &&
             !ip_get_option(ipcb, SOF_REUSEADDR)) {
#else /* SO_REUSE */
    /* port matches that of PCB in list and REUSEADDR not set -> reject */
    else {
#endif /* SO_REUSE */
      if ((ipcb->local_port == port) &&
          /* IP address matches, or one is IP_ADDR_ANY? */
          (ip_addr_isany(&(ipcb->local_ip)) ||
           ip_addr_isany(ipaddr) ||
           ip_addr_cmp(&(ipcb->local_ip), ipaddr))) {
        /* 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 ", port));
        return ERR_USE;
      }
根据源代码的注释,找到编译选项,并且打开
#ifndef SO_REUSE
#define SO_REUSE                        1
编译后测试依然不行,发现上述语句中的if选项始终执行导致
return ERR_USE;
原来是pcb->so_options没有变,我找了一下lwip没有提供修改该选项的接口方法,也许有我没找到,于是手动添加:
        udp_p2p = udp_new();
udp_p2p->so_options|=SOF_REUSEADDR;
最后编译测试成功,主机可以随时修改端口号,本机可以正常向修改端口号的主机发送数据。
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。