STM32+LWIP 实现UDP 组播中遇到的坑
最近在做一个功能,需要用到UDP组播的通讯方式,记录一下做UDP组播的过程,中间遇到了很多坑,GPT和DeepSECK已经问的崩溃了。多的不少 少的不唠,先上程序。
·
最近在做一个功能,需要用到UDP组播的通讯方式,记录一下做UDP组播的过程,中间遇到了很多坑,GPT和DeepSECK已经问的崩溃了。
多的不少 少的不唠,先上程序
启用UDP和组播功能
在lwipopts.h文件中启用组播支持:
#define LWIP_IGMP 1
#define LWIP_UDP 1
#define LWIP_SOCKET 1
初始化UDP组播
在代码中初始化UDP并加入组播组:
#define MULTICAST_PORT 5000
static struct udp_pcb *multicast_pcb = NULL;
static ip_addr_t mutlcast_send_ip;
static ip_addr_t mutlcast_recv_ip;
static void udp_recv_multicast(void *arg, struct udp_pcb *pcb, struct pbuf *p,const ip_addr_t *addr, u16_t port);
static void udp_multicast_init(void)
{
err_t err;
if (multicast_pcb != NULL) return;
multicast_pcb = udp_new();
if (multicast_pcb == NULL)
{
LOGD("udp_pcb fail\r\n");
return;
}
IP4_ADDR(&mutlcast_send_ip, 224, 0, 1, 0); /* 设置发送多播地址 */
IP4_ADDR(&mutlcast_recv_ip, 224, 0, 1, 0); /* 设置加入的多播接收地址 */
/* 加入多播组 */
err = igmp_joingroup(IP_ADDR_ANY, &mutlcast_recv_ip);
if (err != ERR_OK)
{
LOGD("add multicast group fail\r\n");
goto free_udp_pcb;
}
err = udp_bind(multicast_pcb, IP_ADDR_ANY, MULTICAST_PORT); /* 绑定端口 */
if (err != ERR_OK)
{
LOGD("bind port fail\r\n");
goto free_udp_pcb;
}
udp_recv(multicast_pcb, udp_recv_multicast, NULL);
LOGD("multicast init ok\r\n");
return;
free_udp_pcb:
if (multicast_pcb != NULL) {
udp_remove(multicast_pcb); // 释放 UDP PCB
multicast_pcb = NULL; // 重要!指针置空,避免悬垂指针
}
}
实现组播数据接收
定义UDP接收回调函数
#define UDP_RECV_BUF_SIZE 256 // 最大接收长度
#define UDP_RECV_QUEUE_LEN 8 // 队列深度(最大缓存8条)
typedef struct {
uint16_t len;
char data[UDP_RECV_BUF_SIZE];
ip_addr_t src_ip;
uint16_t src_port;
} udp_recv_msg_t;
static QueueHandle_t udp_recv_queue = NULL;
static struct udp_pcb *multicast_pcb = NULL;
void udp_recv_multicast(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
{
if (p == NULL) return;
struct pbuf *q;
int recv_count = 0;
udp_recv_msg_t msg;
memset(&msg, 0, sizeof(msg)); // 清空结构体
// 拷贝数据
for (q = p; q != NULL && recv_count < UDP_RECV_BUF_SIZE; q = q->next)
{
uint16_t copy_len = q->len;
if ((recv_count + copy_len) > UDP_RECV_BUF_SIZE) {
copy_len = UDP_RECV_BUF_SIZE - recv_count;
LOGD("multicast recv overflow, truncated\r\n");
}
memcpy(&msg.data[recv_count], q->payload, copy_len);
recv_count += copy_len;
}
msg.len = recv_count;
msg.src_ip = *addr;
msg.src_port = port;
// 入队
if (udp_recv_queue != NULL)
{
if (xQueueSendFromISR(udp_recv_queue, &msg, NULL) != pdTRUE)
{
LOGD("udp_recv_queue send fail\r\n");
}
}
pbuf_free(p); // 释放 pbuf
}
发送组播数据
通过UDP PCB发送数据到组播地址:
int udp_multicast_send(const void *psrc, uint32_t src_len, uint16_t port)
{
struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, src_len, PBUF_POOL);
if (p == NULL)
{
LOGD("pbuf alloc fail\r\n");
return -1;
}
pbuf_take(p, psrc, src_len);
udp_sendto_if(multicast_pcb, p, &mutlcast_send_ip, port, netif_default);
pbuf_free(p); /* 释放pbuf */
return 0;
}
创建任务处理接收的数据;
void udp_process_task(void *arg)
{
udp_recv_msg_t msg;
while (1)
{
if (xQueueReceive(udp_recv_queue, &msg, portMAX_DELAY) == pdTRUE)
{
uint32_t ip = msg.src_ip.addr;
LOGD("from ip %d.%d.%d.%d:%d data, len:%d\r\n",
ip & 0xFF, (ip >> 8) & 0xFF,
(ip >> 16) & 0xFF, (ip >> 24) & 0xFF,
msg.src_port, msg.len);
parse_multicast_data(msg.data, msg.len);
}
}
}
经过上面的程序基本就可以实现UDP的组播了
注意如果你的设备能够就将组播包发出去但收不到,大概率是在ETH驱动将组播过滤了,设置为
全部接收
ETH_InitStruct->ETH_MulticastFramesFilter = ETH_MulticastFramesFilter_None;
还要注意的是在以太网初始化时要加入组播标志
netif->flags |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_IGMP;
并强制接收所有组播包;
ETH->MACFFR |= ETH_MACFFR_PAM; // 强制接收所有组播包
就可以了
更多推荐



所有评论(0)