做一个本机设备和网络主机设备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\n", 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;
最后编译测试成功,主机可以随时修改端口号,本机可以正常向修改端口号的主机发送数据
|