udp是无连接的传输层协议,没有客户端和服务器的概念。
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;
}
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;
}
下面的这段代码是最关键的,目的就是根据,本地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;
}
}
...//省略部分代码
//回调函数
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);
不可以,因为,没有给远程ip端口号赋值
不能,根据udp_input的逻辑,远程ip和端口号,第一步可以通过,但是设备udppcb的远程ip和端口号,是不匹配的,所以这一帧数据将会被丢弃
如何才可以收到回复,删掉第4步,这样的话,就是只会判断远程的端口号是否正确,而不会关注,是那个ip发过来的数据。这种方式十分好用,通常是作为服务器时,任何客户端ip通过该端口发过来的数据,都会得到处理