概念先知道
- UDP 服务器:UDP 没有“连接”,服务器只需把套接字绑定(bind)到端口,就可以收发任意来源的数据报。
- 数据报(datagram):UDP 每次收发是一个完整的数据包,边界保留;不像 TCP 是字节流。
- recvfrom / sendto:UDP 收发函数比 TCP 多两个参数,用来携带对方的地址,这样才知道“谁发来的、回给谁”。
- 回显(echo):例程把收到的数据原样发回给发送方,用来验证双向收发。
例程功能简介
本页对应博流官方 SDK 的 wifi_udp 例程中的 UDP 回显服务器部分(examples/wifi/sta/wifi_udp/wifi_udp_echo.c):
socket(AF_INET, SOCK_DGRAM, 0)创建 UDP 套接字,bind()绑定0.0.0.0:port;- 循环
recvfrom()接收数据报,打印来源 IP 与内容,再sendto()原样回给发送方; - shell 命令
wifi_udp_echo <port>启动服务器,Ctrl+C退出并打印收发统计。 - 同族参考:UDP 客户端与广播/组播没有独立例程,教程提供自编 UDP 客户端、UDP 广播、UDP 组播示例。
操作步骤
在终端进入 SDK 的 UDP 例程目录(前提:环境已按快速开始(Linux)或Windows搭好):
cd examples/wifi/sta/wifi_udp统一填写 bl616:
make CHIP=bl616 BOARD=bl616dk按住 BOOT 键短按 EN/RST 进入下载模式后烧录:
make flash CHIP=bl616 COMX=/dev/ttyUSB0串口助手波特率 2000000,看到 bouffalolab /> 后连接路由器,再启动 UDP 回显服务器监听 3365 端口:
wifi_sta_connect Your_SSID 12345678
wifi_udp_echo 3365在电脑上用 netcat 的 UDP 模式向模组发包,模组收到后原样回显:
nc -uv 192.168.1.3 3365代码执行流程
UDP 服务器从启动到回显的完整流程如下:
例程调用的 API 介绍
socket(AF_INET, SOCK_DGRAM, 0)
创建 UDP 套接字。SOCK_DGRAM(数据报)是 UDP 与 TCP 的关键区别。
参数:
AF_INET:IPv4 地址族SOCK_DGRAM:无连接的数据报套接字0:协议自动选择
返回值:大于等于 0 的句柄;< 0 失败
bind(sock, addr, len)
把套接字绑定到本地端口(例程 0.0.0.0:3365),之后该端口收到的数据报都会进这个套接字。
参数:
sock:socket 句柄addr:sockaddr_in,sin_port = htons(port)、sin_addr = INADDR_ANYlen:地址结构长度
返回值:0 成功;-1 失败
recvfrom(sock, buf, len, 0, from, fromlen)
阻塞接收一个数据报,并把发送方地址写入 from。UDP 没有连接,每次都要从 from 知道对方是谁。
参数:
sock:套接字句柄buf/len:接收缓冲区与长度(例程 1024 字节)from/fromlen:发送方地址与长度
返回值:实际接收字节数;< 0 出错
sendto(sock, buf, len, 0, to, tolen)
向指定地址发送一个数据报。回显时把 recvfrom 得到的 from 原样传回即可。
参数:
sock:套接字句柄buf/len:数据与长度to/tolen:目标地址与长度
返回值:实际发送字节数;< 0 出错
完整代码
以下为 wifi_udp_echo.c 完整源码,与官方示例(examples/wifi/sta/wifi_udp)一致,默认折叠,点击展开:
📜 点击展开 wifi_udp/main.c 完整代码
/****************************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include "FreeRTOS.h"
#include "task.h"
#include "timers.h"
#include <lwip/tcpip.h>
#include <lwip/sockets.h>
#include <lwip/netdb.h>
#include "wifi_mgmr_ext.h"
#include "bflb_irq.h"
#include "bflb_uart.h"
#include "rfparam_adapter.h"
#include "board.h"
#include "shell.h"
#ifndef BL602
#include "fhost_api.h"
#include "wifi_mgmr.h"
#endif
#include "async_event.h"
#include "mm.h"
#define DBG_TAG "MAIN"
#include "log.h"
struct bflb_device_s *gpio;
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define WIFI_STACK_SIZE (1536)
#define TASK_PRIORITY_FW (16)
/****************************************************************************
* Private Types
****************************************************************************/
/****************************************************************************
* Private Data
****************************************************************************/
static struct bflb_device_s *uart0;
extern void shell_init_with_task(struct bflb_device_s *shell);
extern void wifi_event_handler(async_input_event_t ev, void *priv);
#ifdef BL602
extern void wifi_task_create(void);
extern int fhost_init(void);
extern int wifi_mgmr_task_start(void);
#endif
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/****************************************************************************
* Functions
****************************************************************************/
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);
}
void wifi_event_handler(async_input_event_t ev, void *priv)
{
uint32_t code = ev->code;
switch (code) {
case CODE_WIFI_ON_INIT_DONE: {
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_INIT_DONE\r\n", __func__);
wifi_mgmr_task_start();
} break;
case CODE_WIFI_ON_MGMR_DONE: {
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_MGMR_DONE\r\n", __func__);
} break;
case CODE_WIFI_ON_SCAN_DONE: {
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_SCAN_DONE\r\n", __func__);
wifi_mgmr_sta_scanlist();
} break;
case CODE_WIFI_ON_CONNECTED: {
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_CONNECTED\r\n", __func__);
void mm_sec_keydump();
mm_sec_keydump();
} break;
case CODE_WIFI_ON_GOT_IP: {
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_GOT_IP\r\n", __func__);
LOG_I("[SYS] Memory left is %d Bytes\r\n", kfree_size(0));
} break;
case CODE_WIFI_ON_DISCONNECT: {
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_DISCONNECT\r\n", __func__);
} break;
case CODE_WIFI_ON_AP_STARTED: {
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_AP_STARTED\r\n", __func__);
} break;
case CODE_WIFI_ON_AP_STOPPED: {
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_AP_STOPPED\r\n", __func__);
} break;
case CODE_WIFI_ON_AP_STA_ADD: {
LOG_I("[APP] [EVT] [AP] [ADD] %lld\r\n", xTaskGetTickCount());
} break;
case CODE_WIFI_ON_AP_STA_DEL: {
LOG_I("[APP] [EVT] [AP] [DEL] %lld\r\n", xTaskGetTickCount());
} break;
default: {
LOG_I("[APP] [EVT] Unknown code %u \r\n", code);
}
}
}
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;
}
LOG_I("PHY RF init success!\r\n");
tcpip_init(NULL, NULL);
xTaskCreate(wifi_start_firmware_task, "wifi init", 1024, NULL, 10, NULL);
vTaskStartScheduler();
while (1) {
}
}📜 点击展开 wifi_udp_echo.c 完整代码
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/socket.h>
#include <lwip/api.h>
#include <lwip/arch.h>
#include <lwip/opt.h>
#include <lwip/inet.h>
#include <lwip/errno.h>
#include <netdb.h>
#include "shell.h"
#include "utils_getopt.h"
#include "bflb_mtimer.h"
// clang-format off
uint32_t recv_buf[2 * 1024] = {0};
// clang-format on
shell_sig_func_ptr abort_exec;
uint64_t recv_len = 0;
int sock = -1;
static void test_close(int sig)
{
if (sock) {
closesocket(sock);
}
abort_exec(sig);
if (recv_len > 0) {
printf("Total send data=%lld\r\n", recv_len);
}
}
#define PING_USAGE \
"wifi_udp_echo [port]\r\n" \
"\t port: local listen port, default port 5001\r\n"
static void wifi_test_udp_echo_init(int argc, char **argv)
{
abort_exec = shell_signal(SHELL_SIGINT, test_close);
printf("udp server task start ...\r\n");
char *port;
struct sockaddr_in udp_addr, remote_addr;
socklen_t addr_len;
if (argc < 2) {
printf("%s", PING_USAGE);
return;
}
/* get port number (argv[1] if present) */
if (argc > 1) {
port = argv[1];
} else {
port = "5001";
}
memset(&recv_buf[0], 0, sizeof(recv_buf));
while (1) {
if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
printf("udp create socket error\r\n");
return;
}
udp_addr.sin_family = AF_INET;
udp_addr.sin_port = htons(atoi(port));
udp_addr.sin_addr.s_addr = INADDR_ANY;
memset(&(udp_addr.sin_zero), 0, sizeof(udp_addr.sin_zero));
printf("Server ip Address : %s:%s\r\n", udp_addr.sin_addr.s_addr, port);
if (bind(sock, (struct sockaddr *)&udp_addr, sizeof(struct sockaddr)) != 0) {
printf("udp bind falied!\r\n");
closesocket(sock);
return;
}
printf("udp bind port success!\r\n");
printf("Press CTRL-C to exit.\r\n");
recv_len = 0;
while (1) {
recv_len = recvfrom(sock, recv_buf, 1024, 0, (struct sockaddr *)&remote_addr, &addr_len);
printf("recv from %s\r\n", inet_ntoa(remote_addr.sin_addr));
printf("recv:%s \r\n", recv_buf);
sendto(sock, recv_buf, recv_len, 0, (struct sockaddr *)&remote_addr, addr_len);
}
closesocket(sock);
return;
}
}
#ifdef CONFIG_SHELL
#include <shell.h>
int cmd_wifi_udp_echo(int argc, char **argv)
{
wifi_test_udp_echo_init(argc, argv);
return 0;
}
SHELL_CMD_EXPORT_ALIAS(cmd_wifi_udp_echo, wifi_udp_echo, wifi udp test);
#endifFAQ
nc -uv 连不上 / 收不到回显
确认模组与电脑在同一网段,且先执行了 wifi_sta_connect 拿到 IP;UDP 无连接,nc 的“Connection succeeded”仅表示本端 socket 就绪;回显数据要等模组收到后再看,可先在电脑上多发包(如 echo hi | nc -uv 192.168.1.3 3365)。
遇到问题?
如有其他问题,请到统一的提问与讨论区:Ai-Thinker Discussions

