Skip to content

概念先知道

  • UDP 广播(Broadcast):把数据报发给“同一局域网内的所有设备”,目标地址是 255.255.255.255(全网段广播)或 192.168.1.255 这样的网段广播地址。
  • 适用场景:局域网设备发现(“谁在线?”)、无中心的服务公告、局域网校时等。广播不会跨路由器转发。
  • SO_BROADCAST:出于安全考虑,lwIP 默认不允许发送广播,必须先用 setsockopt(SOL_SOCKET, SO_BROADCAST) 打开。
  • 接收广播:要接收广播/回复,需要把套接字绑定到对应端口,并同样打开 SO_BROADCAST

例程功能简介

SDK 无 UDP 广播例程,本页自编示例wifi_tcp 例程骨架上编写:

  • 拿到 IP 后创建 udp_broadcast_task
  • 打开 SO_BROADCAST 选项并绑定本地 6000 端口;
  • 每 2 秒向 255.255.255.255:6000 广播 hello broadcast
  • 同时接收局域网内的广播/回复并打印,验证“一发多收”。
  • 同族参考:向固定一组设备发送用 UDP 组播,点对点用 UDP 客户端

操作步骤

1
进入例程目录

SDK 没有 UDP 广播例程,本页在 wifi_tcp 例程骨架上编写(标注自编示例)。先进入目录:

cd examples/wifi/sta/wifi_tcp
2
替换 main.c

用下面的自编 main.c 覆盖例程目录里的 main.c

3
编译工程

统一填写 bl616

make CHIP=bl616 BOARD=bl616dk
4
烧录固件

按住 BOOT 短按 EN/RST 进入下载模式后烧录:

make flash CHIP=bl616 COMX=/dev/ttyUSB0
5
连接 Wi-Fi 并验证

串口助手波特率 2000000,连接路由器后程序自动开始:每 2 秒向 255.255.255.255:6000 广播一次,并打印收到的广播/回复。可在同一 Wi-Fi 下的另一台电脑上运行 nc -u -l 6000 观察。

wifi_sta_connect Your_SSID 12345678

代码执行流程

UDP 广播从启动到收发的完整流程如下:

例程调用的 API 介绍

setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt))

打开广播发送权限,opt = 1 使能。不做这一步,sendto 广播地址会失败。

参数

  • sock:套接字句柄
  • SO_BROADCAST:选项名
  • &optint1 表示允许

返回值0 成功;-1 失败

bind(sock, addr, len)

把套接字绑定到本地端口(示例 0.0.0.0:6000),这样才能收到发往该端口的广播与回复。

参数

  • sock:套接字句柄
  • addrsockaddr_in,端口 htons(6000)、地址 INADDR_ANY
  • len:地址结构长度

返回值0 成功;-1 失败

sendto / recvfrom(sock, buf, len, 0, addr, len)

发送时目标地址填广播地址 255.255.255.255:6000;接收时 recvfrom 返回发送方地址,方便区分是哪台设备发来的。

参数

  • sock:套接字句柄
  • buf / len:数据与长度
  • addr / len:目标(发送)/ 来源(接收)地址

返回值:实际收/发字节数;< 0 出错

完整代码

以下为自编示例 main.c(基于 SDK wifi_tcp 例程骨架编写,非 SDK 自带文件),默认折叠,点击展开:

📜 点击展开 自编 udp_broadcast main.c 完整代码
c
/*
 * 自编示例:UDP 广播(基于 lwIP socket)
 * 使用方法:
 *   1. 用本文件覆盖 examples/wifi/sta/wifi_tcp/main.c;
 *   2. make CHIP=bl616 BOARD=bl616dk && make flash CHIP=bl616 COMX=/dev/ttyUSB0;
 *   3. 串口执行 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_BROADCAST_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_broadcast_task(void *param)
{
    int sock;
    int opt = 1;
    struct sockaddr_in local, bcast, remote;
    socklen_t addr_len = sizeof(remote);
    char send_buf[] = "hello broadcast";
    char recv_buf[128];

    /* 等待 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;
    }

    /* 允许发送广播 */
    if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt)) < 0) {
        LOG_E("set SO_BROADCAST failed\r\n");
    }

    /* 绑定本地端口,用于接收广播与回复 */
    memset(&local, 0, sizeof(local));
    local.sin_family = AF_INET;
    local.sin_port = htons(UDP_BROADCAST_PORT);
    local.sin_addr.s_addr = INADDR_ANY;
    if (bind(sock, (struct sockaddr *)&local, sizeof(local)) < 0) {
        LOG_E("bind failed\r\n");
    }

    /* 广播地址:255.255.255.255 代表当前网段全部设备 */
    memset(&bcast, 0, sizeof(bcast));
    bcast.sin_family = AF_INET;
    bcast.sin_port = htons(UDP_BROADCAST_PORT);
    bcast.sin_addr.s_addr = inet_addr("255.255.255.255");

    LOG_I("UDP broadcast start, port %d\r\n", UDP_BROADCAST_PORT);

    while (1) {
        int ret = sendto(sock, send_buf, strlen(send_buf), 0,
                         (struct sockaddr *)&bcast, sizeof(bcast));
        if (ret < 0) {
            LOG_E("sendto broadcast failed\r\n");
        } else {
            LOG_I("broadcast send %d bytes\r\n", ret);
        }

        ret = recvfrom(sock, recv_buf, sizeof(recv_buf) - 1, 0,
                       (struct sockaddr *)&remote, &addr_len);
        if (ret >= 0) {
            recv_buf[ret] = '\0';
            LOG_I("recv %d bytes from %s: %s\r\n",
                  ret, inet_ntoa(remote.sin_addr), recv_buf);
        }

        vTaskDelay(pdMS_TO_TICKS(2000));
    }
}

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_broadcast_task, "udp bcast", 1024, NULL, 11, NULL);

    vTaskStartScheduler();

    while (1) {
    }
}

FAQ

sendto 广播地址报错

确认 setsockopt(SOL_SOCKET, SO_BROADCAST) 执行成功;lwIP 默认禁止广播发送,没有打开该选项会返回错误。

其他设备收不到广播

广播只在同一局域网(同一路由器/交换机)内传播,不同网段收不到;路由器开启“AP 隔离”时客户端之间互相隔离,需关闭该功能。

遇到问题?

如有其他问题,请到统一的提问与讨论区:Ai-Thinker Discussions

Released under the MIT License. Build Time 2026-09-11 14:52:23