1 UDP协议报文结构
2 UDP数据递交流程
用户发送数据为例:
(1)申请一个pbuf。(pbuf游走于应用层、传输层、网络层)。
struct pbuf {
struct pbuf *next; // 指向链表中的下一个pbuf
void *payload; // 指向数据缓冲区的指针
u16_t len; // 缓冲区的长度
u16_t tot_len; // 整个数据包的总长度(如果pbuf是链表的一部分)
// ... 可能还有其他字段
};
(2)拷贝(把用户数据拷贝到pbuf的数据区)。
(3)调用RAW相关接口将pbuf递交给传输层。
(4)在传输层设置UDP相关字段,递交给网络层。
(5)在网络层添加IP的首部。
3 UDP控制块结构
struct udp_pcb {
IP_PCB; /* 通用IP控制块 */
struct udp_pcb *next; /* 下一个节点的指针,用于构成控制块链表 */
u8_t flags; /* 控制块状态 */
u16_t local_port, remote_port; /* 本地/远程端口号 */
udp_recv_fn recv;/* 处理网络接收数据的回调 */
void *recv_arg; /* 用户自定义参数,接收回调入参 */
#if LWIP_MULTICAST_TX_OPTIONS
#if LWIP_IPV4
/** outgoing network interface for multicast packets, by IPv4 address (if not 'any') */
ip4_addr_t mcast_ip4;
#endif /* LWIP_IPV4 */
/** outgoing network interface for multicast packets, by interface index (if nonzero) */
u8_t mcast_ifindex;
/** TTL for outgoing multicast packets */
u8_t mcast_ttl;
#endif /* LWIP_MULTICAST_TX_OPTIONS */
#if LWIP_UDPLITE
/** used for UDP_LITE only */
u16_t chksum_len_rx, chksum_len_tx;
#endif /* LWIP_UDPLITE */
};
LWIP支持多个UDP连接,所以用next指针将这些连接的端口连起来管理。
flags:连接状态、非连接状态
udp_input函数:获取pbuf的UDP的首部,然后遍历UDP的控制块链表(单向链表),判断报文的端口与链表中某个控制块的本地端口是否一致,如果一致,就会将pbuf递交给这个控制块,该控制块将pbuf递交给回调函数处理。
4 UDP控制块原理
链接:多个UDP控制块构建一个单向的链表;
回调函数:每一个控制块都管理一个接受回调函数(此函数可以相同,可以不同);
端口:LWIP内核根据端口号查找控制块;
描述:每一个控制块用来描述各自网络的的信息(一个控制块代表一个UDP连接)。
5 RAW接口的UDP相关函数
函数 | 解释 |
udp_new | 新建一个UDP的PCB控制块 |
udp_remove | 将PCB控制块从链表中删除 |
udp_bind | 为UDP的PCB控制块绑定一个本地IP地址和端口号 |
udp_connect | 连接到指定IP地址主机的指定端口 |
udp_disconnect | 断开连接,将控制块设置为非连接状态 |
udp_send | 通过有一个PCB控制块发送数据 |
udp_recv | 创建的回调函数 |
6 (RAW)UDP连接配置流程
(1)udp_new 创建一个UDP控制块 ; //描述当前UDP的端口、IP地址等
(2)udp_connect设置目标IP地址和插入UDP PCB链表 ;//发送给谁
(3)udp_bind绑定本地IP地址和端口;//LWIP内核根据端口号把数据发至那个UDP控制块
(4)udp_recv注册接收回调函数;//接受回调函数自行编写(需要参考相关例程)
(5)udp_send发送函数;//网络搭建完成后,收发数据
7 udp代码
建立.c文件,添加代码。
#include "ip_addr.h"
#include "ip4_addr.h"
#include "err.h"
#include "udp.h"
#include "stdio.h"
#include "udp_client.h"
#include "string.h"
#include "pbuf.h"
#define UDP_REMOTE_PORT 7
#define UDP_LOCAL_PORT 7
/*配置UDP客户端,这里需要知道服务器的地址*/
static struct udp_pcb *upcb_client;
char *pdata = "udp test\r\n";
uint8_t recv_flag = 0;
void udp_client_init()
{
ip_addr_t serverIP;
err_t err;
IP4_ADDR(&serverIP,192,168,2,87); /* 绑定服务器的地址 */
/* 本地端口 */
upcb_client->local_port = UDP_LOCAL_PORT;
/* 创建udp控制块 */
upcb_client = udp_new();
if(upcb_client != NULL)
{
printf("udp 控制块创建成功\r\n");
/* 连接指定ip和端口*/
err = udp_connect(upcb_client,&serverIP,UDP_REMOTE_PORT);
if(err == ERR_OK)
{
printf("udp_connect连接远程IP、端口成功\r\n");
/* 绑定本地IP地址和端口号 */
err = udp_bind(upcb_client,IP_ADDR_ANY,UDP_LOCAL_PORT);
if(err == ERR_OK)
{
printf("udp_bind成功\r\n");
/* 注册接收回调函数 */
udp_recv(upcb_client,udp_client_receive_callback,NULL);
printf("注册就接受回调函数成功\r\n");
udp_client_send(pdata);
}
}
else
{
udp_remove(upcb_client);
printf("connect udp pcb faild\r\n");
}
}
}
u8_t group[100] = {0};
void udp_client_receive_callback(void *arg,struct udp_pcb *pcb,struct pbuf *p,const ip_addr_t *addr, u16_t port)
{
int i ;
/* 数据回传 */
// udp_send(pcb, p);
/* 打印端口 */
printf("server ip:%d.%d.%d.%d port:%d\r\n", (addr->addr) & 0xff, ((addr->addr)>>8) & 0xff,((addr->addr)>>16) & 0xff,(addr->addr)>>24,port);
/* 打印接收到的数据 */
if(p != NULL)
{
struct pbuf *ptemp = p;
while(ptemp != NULL)
{
for(i = 0; i< ptemp->len;i++)
{
printf("%c",*((char *)ptemp->payload +i));
}
ptemp = ptemp->next;
}
printf("\r\n");
}
pbuf_free(p);
}
void udp_client_send(char *pdata)
{
struct pbuf *p;
/* 分配缓冲区空间 */
p = pbuf_alloc(PBUF_TRANSPORT,strlen(pdata),PBUF_POOL);
if(p != NULL)
{
/* 填充缓冲区数据 */
pbuf_take(p,pdata,strlen(pdata));
/* 发送udp数据包 */
udp_send(upcb_client,p);
/* 释放缓冲区 */
pbuf_free(p);
}
}