概念先知道
- UDP 客户端:主动向服务器“扔”数据报的一方。UDP 不需要建立连接,
sendto指定目标地址即可发送。 - 数据报:UDP 每次收发是一个完整数据包,边界保留;不保证送达、不保证顺序,适合容忍少量丢失的实时场景。
- sendto / recvfrom:发送要带目标地址,接收要带对方地址;因为没有连接,每次都要显式告诉内核“发给谁 / 谁发来的”。
- UDP 端口:服务器必须监听某个端口,客户端的源端口由系统随机分配。
例程功能简介
SDK 无独立 UDP 客户端例程,本页自编示例在 wifi_tcp 例程骨架上编写(网络部分与官方 wifi_udp 例程同样基于 lwIP socket):
- 拿到 IP 后创建
udp_client_task; socket(AF_INET, SOCK_DGRAM, 0)创建 UDP 套接字;- 每 1 秒向
UDP_SERVER_IP:6000发送hello from ai-m6x,同时接收服务器回复并打印; - 修改文件开头的
UDP_SERVER_IP/UDP_SERVER_PORT即可切换目标。 - 同族参考:官方
wifi_udp例程(UDP 服务器页)演示了服务器/回显端。
操作步骤
SDK 没有独立的 UDP 客户端例程(wifi_udp 只有回显服务器),本页在 wifi_tcp 例程骨架上编写(标注自编示例)。先进入目录:
cd examples/wifi/sta/wifi_tcp用下面的自编 main.c 覆盖例程目录里的 main.c(记得把文件里的 UDP_SERVER_IP 改成你的服务器 IP):
统一填写 bl616:
make CHIP=bl616 BOARD=bl616dk按住 BOOT 短按 EN/RST 进入下载模式后烧录:
make flash CHIP=bl616 COMX=/dev/ttyUSB0串口助手波特率 2000000,连接路由器后程序自动开始:每 1 秒向 UDP_SERVER_IP:6000 发送一次 hello from ai-m6x,并打印服务器回复。电脑上可先启动一个 UDP 回显服务(如 nc -ul 6000 或 nc -u -l 6000)观察。
wifi_sta_connect Your_SSID 12345678代码执行流程
UDP 客户端从启动到收发的完整流程如下:
例程调用的 API 介绍
socket(AF_INET, SOCK_DGRAM, 0)
创建 UDP 套接字。
参数:
AF_INET:IPv4 地址族SOCK_DGRAM:数据报套接字(UDP)0:协议自动选择
返回值:大于等于 0 的句柄;< 0 失败
sendto(sock, buf, len, 0, to, tolen)
向指定地址发送一个数据报。
参数:
sock:套接字句柄buf/len:数据与长度to/tolen:目标地址(服务器 IP 与端口)
返回值:实际发送字节数;< 0 出错
recvfrom(sock, buf, len, 0, from, fromlen)
阻塞接收一个数据报并返回发送方地址。示例把它当作“等待服务器回复”。
参数:
sock:套接字句柄buf/len:接收缓冲区与长度from/fromlen:发送方地址与长度
返回值:实际接收字节数;< 0 出错
完整代码
以下为自编示例 main.c(基于 SDK wifi_tcp 例程骨架编写,非 SDK 自带文件),默认折叠,点击展开:
📜 点击展开 自编 udp_client main.c 完整代码
/*
* 自编示例:UDP 客户端(基于 lwIP socket)
* 使用方法:
* 1. 用本文件覆盖 examples/wifi/sta/wifi_tcp/main.c;
* 2. 把 UDP_SERVER_IP 改成你的服务器 IP;
* 3. make CHIP=bl616 BOARD=bl616dk && make flash CHIP=bl616 COMX=/dev/ttyUSB0;
* 4. 串口执行 wifi_sta_connect <SSID> <密码>,程序自动发送/接收。
*/
#include "FreeRTOS.h"
#include "task.h"
#include "timers.h"
#include <string.h>
#include <lwip/tcpip.h>
#include <lwip/sockets.h>
#include <lwip/netdb.h>
#include "wifi_mgmr_ext.h"
#include "fhost_api.h"
#include "wifi_mgmr.h"
#include "bflb_irq.h"
#include "bflb_uart.h"
#include "rfparam_adapter.h"
#include "async_event.h"
#include "mm.h"
#include "board.h"
#include "shell.h"
#define DBG_TAG "MAIN"
#include "log.h"
#define UDP_SERVER_IP "192.168.1.2"
#define UDP_SERVER_PORT 6000
static volatile uint32_t wifi_state = 0;
static struct bflb_device_s *uart0;
extern void shell_init_with_task(struct bflb_device_s *shell);
void wifi_event_handler(async_input_event_t ev, void *priv)
{
switch (ev->code) {
case CODE_WIFI_ON_GOT_IP:
wifi_state = 1;
LOG_I("Got IP\r\n");
break;
case CODE_WIFI_ON_DISCONNECT:
wifi_state = 0;
LOG_I("WiFi disconnected\r\n");
break;
default:
break;
}
}
static void udp_client_task(void *param)
{
int sock;
struct sockaddr_in server;
char send_buf[] = "hello from ai-m6x";
char recv_buf[128];
socklen_t addr_len = sizeof(server);
/* 等待 Wi-Fi 拿到 IP */
while (!wifi_state) {
vTaskDelay(pdMS_TO_TICKS(200));
}
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0) {
LOG_E("create socket failed\r\n");
vTaskDelete(NULL);
return;
}
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(UDP_SERVER_PORT);
server.sin_addr.s_addr = inet_addr(UDP_SERVER_IP);
LOG_I("UDP client send to %s:%d\r\n", UDP_SERVER_IP, UDP_SERVER_PORT);
while (1) {
int ret = sendto(sock, send_buf, strlen(send_buf), 0,
(struct sockaddr *)&server, sizeof(server));
if (ret < 0) {
LOG_E("sendto failed\r\n");
} else {
LOG_I("send %d bytes\r\n", ret);
}
ret = recvfrom(sock, recv_buf, sizeof(recv_buf) - 1, 0,
(struct sockaddr *)&server, &addr_len);
if (ret >= 0) {
recv_buf[ret] = '\0';
LOG_I("recv %d bytes: %s\r\n", ret, recv_buf);
}
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
static void wifi_start_firmware_task(void *param)
{
LOG_I("Starting wifi ...\r\n");
async_register_event_filter(EV_WIFI, wifi_event_handler, NULL);
wifi_task_create();
LOG_I("Starting fhost ...\r\n");
fhost_init();
vTaskDelete(NULL);
}
int main(void)
{
board_init();
uart0 = bflb_device_get_by_name("uart0");
shell_init_with_task(uart0);
if (0 != rfparam_init(0, NULL, 0)) {
LOG_I("PHY RF init failed!\r\n");
return 0;
}
tcpip_init(NULL, NULL);
xTaskCreate(wifi_start_firmware_task, "wifi init", 1024, NULL, 10, NULL);
xTaskCreate(udp_client_task, "udp client", 1024, NULL, 11, NULL);
vTaskStartScheduler();
while (1) {
}
}FAQ
服务器收不到数据
确认模组已拿到 IP(日志出现 Got IP);UDP_SERVER_IP 必须改成服务器实际 IP 并重新编译烧录;服务器防火墙可能拦截 UDP,先在同网段用 nc -u -l 6000 验证。
recvfrom 一直阻塞、打印不了回复
UDP 不保证送达,服务器没回包时 recvfrom 会一直等。可以给套接字设置接收超时(setsockopt(SO_RCVTIMEO)),或先只发送、把接收注释掉联调。
遇到问题?
如有其他问题,请到统一的提问与讨论区:Ai-Thinker Discussions

