【LwIP - UDP】 - udp_bind和udp_connect分析

2019-07-14 10:50发布

目录 前言 1 udp_bind 2  udp_connect  

前言

udp_bind和udp_connect两者具体的工作原理,笔者在网上找不到正确的说法。对此,笔者主要对UDP中的这两个接口进行分析。

1 udp_bind

udp_bind将一个UDP PCB与IP和端口进行绑定。当然不是简简单单的把该IP和端口记录到UDP PCB中。会做如下的操作: (1)  判断UDP PCB是否早存在于链表中,同时也判断要绑定的端口port是否已经被链表中的某一个PCB绑定且IP是否一致。只要端口和IP没有被链表中的PCB绑定,则能正常的为新的UDP PCB绑定或者已有的UDP PCB重绑定。 (2)  第(1)通过的话,会为UDP PCB设置IP,以及端口。至于端口,如果port为0,则会自动分配一个端口。 (3) UDP PCB如果是新的PCB,则将入UDP PCB链表中。 /** * Bind an UDP PCB. * * @param pcb UDP PCB to be bound with a local address ipaddr and port. * @param ipaddr local IP address to bind with. Use IP_ADDR_ANY to * bind to all local interfaces. * @param port local UDP port to bind with. Use 0 to automatically bind * to a random port between UDP_LOCAL_PORT_RANGE_START and * UDP_LOCAL_PORT_RANGE_END. * * ipaddr & port are expected to be in the same byte order as in the pcb. * * @return lwIP error code. * - ERR_OK. Successful. No error occured. * - ERR_USE. The specified ipaddr and port are already bound to by * another UDP PCB. * * @see udp_disconnect() */ err_t udp_bind(struct udp_pcb *pcb, ip_addr_t *ipaddr, u16_t port) { struct udp_pcb *ipcb; u8_t rebind; // [2018年9月7日14:52:39] 下列3行调试信息 LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_bind(ipaddr = ")); ip_addr_debug_print(UDP_DEBUG, ipaddr); LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, (", port = %"U16_F") ", port)); rebind = 0; /* Check for double bind and rebind of the same pcb */ // [2018年9月7日15:00:02] 检查相同的PCB双重绑定和重绑定 for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) { /* is this UDP PCB already on active list? */ // [2018年9月7日15:02:31] 判断这个UDP PCB是否早存在于链表中 if (pcb == ipcb) { /* pcb may occur at most once in active list */ LWIP_ASSERT("rebind == 0", rebind == 0); /* pcb already in list, just rebind */ // [2018年9月7日15:03:43] PCB早存在于链表中,则重绑定 rebind = 1; } /* By default, we don't allow to bind to a port that any other udp PCB is alread bound to, unless *all* PCBs with that port have tha REUSEADDR flag set. */ // [2018年9月7日15:14:02] 默认情况下,我们不允许一个已被绑定过的端口再次绑定,除非PCB设置了REUSEADDR标志 #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 */ // [2018年9月7日15:17:40] port与链表中的PCB进行匹配,并且REUSEADDR没有设置 else { #endif /* SO_REUSE */ // [2018年9月7日15:21:35] port与链表中的PCB的local_port匹配 if ((ipcb->local_port == port) && /* IP address matches, or one is IP_ADDR_ANY? */ // [2018年9月7日15:36:43] IP地址匹配或者是一个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 */ // 已经有其它的PCB早已绑定了该IP和端口 LWIP_DEBUGF(UDP_DEBUG, ("udp_bind: local port %"U16_F" already bound by another pcb ", port)); return ERR_USE; } } } // IP或者端口没被其它PCB绑定 ip_addr_set(&pcb->local_ip, ipaddr); /* no port specified? */ // [2018年9月7日15:09:59] 没有指定端口 if (port == 0) { // [2018年9月7日15:10:23] 自动分配端口 port = udp_new_port(); if (port == 0) { /* no more ports available in local range */ // [2018年9月7日15:10:37] 返回的端口为0,则没有有效的端口了 LWIP_DEBUGF(UDP_DEBUG, ("udp_bind: out of free UDP ports ")); return ERR_USE; } } // [2018年9月7日15:11:23] 为PCB块设置端口号 pcb->local_port = port; snmp_insert_udpidx_tree(pcb); /* pcb not active yet? */ // 该PCB未有效,即未加入链表,则将其加入链表 if (rebind == 0) { /* place the PCB on the active list if not already there */ pcb->next = udp_pcbs; udp_pcbs = pcb; } LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("udp_bind: bound to %"U16_F".%"U16_F".%"U16_F".%"U16_F", port %"U16_F" ", ip4_addr1_16(&pcb->local_ip), ip4_addr2_16(&pcb->local_ip), ip4_addr3_16(&pcb->local_ip), ip4_addr4_16(&pcb->local_ip), pcb->local_port)); return ERR_OK; }

 

2  udp_connect

udp_connect将一个UDP PCB与远端IP和端口进行绑定。如果UDP PCB是新分配的,则默认绑定到本地地址以及自动分配的端口。如果UDP PCB已经绑定过端口,则会重绑定远端IP和端口。如果用户如果要重新绑定本地地址和端口,可在udp_connect之前或者之后调用udp_bind()即可。udp_connect的操作不多,如下: (1) 检查UDP PCB是否已绑定本地端口。如果本地端口为0(即没有绑定),则调用udp_bind()自动分配一个端口并绑定。 (2) 绑定远端IP和端口,并将UDP PCB的标志设置为UDP_FLAGS_CONNECTED。 (3) 将UDP PCB加入UDP PCBs链表中,如果早已存在则忽略。 /** * Connect an UDP PCB. * * This will associate the UDP PCB with the remote address. * * @param pcb UDP PCB to be connected with remote address ipaddr and port. * @param ipaddr remote IP address to connect with. * @param port remote UDP port to connect with. * * @return lwIP error code * * ipaddr & port are expected to be in the same byte order as in the pcb. * * The udp pcb is bound to a random local port if not already bound. * * @see udp_disconnect() */ err_t udp_connect(struct udp_pcb *pcb, ip_addr_t *ipaddr, u16_t port) { struct udp_pcb *ipcb; if (pcb->local_port == 0) { // [2018年9月7日16:35:58] // 如果pcb是新分配的,则pcb采用本地IP以及自动分配的端口 err_t err = udp_bind(pcb, &pcb->local_ip, pcb->local_port); if (err != ERR_OK) { return err; } } ip_addr_set(&pcb->remote_ip, ipaddr); // [2018年9月7日16:33:19] 为UDP PCB设置远程IP pcb->remote_port = port; // [2018年9月7日16:33:19] 为UDP PCB设置端口 pcb->flags |= UDP_FLAGS_CONNECTED; /** TODO: this functionality belongs in upper layers */ #ifdef LWIP_UDP_TODO /* Nail down local IP for netconn_addr()/getsockname() */ if (ip_addr_isany(&pcb->local_ip) && !ip_addr_isany(&pcb->remote_ip)) { struct netif *netif; if ((netif = ip_route(&(pcb->remote_ip))) == NULL) { LWIP_DEBUGF(UDP_DEBUG, ("udp_connect: No route to 0x%lx ", pcb->remote_ip.addr)); UDP_STATS_INC(udp.rterr); return ERR_RTE; } /** TODO: this will bind the udp pcb locally, to the interface which is used to route output packets to the remote address. However, we might want to accept incoming packets on any interface! */ pcb->local_ip = netif->ip_addr; } else if (ip_addr_isany(&pcb->remote_ip)) { pcb->local_ip.addr = 0; } #endif LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("udp_connect: connected to %"U16_F".%"U16_F".%"U16_F".%"U16_F",port %"U16_F" ", ip4_addr1_16(&pcb->local_ip), ip4_addr2_16(&pcb->local_ip), ip4_addr3_16(&pcb->local_ip), ip4_addr4_16(&pcb->local_ip), pcb->local_port)); /* Insert UDP PCB into the list of active UDP PCBs. */ // 将UDP PCB插入有效的UDP PCBs链表中(当然,如果前面调用udp_bind()且正常返回,其会将PCB插入链表) 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; }