news 2026/6/9 16:39:07

28 鸿蒙LiteOS RK2206 LwIP Raw API 实现无阻塞UDP双向通信

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
28 鸿蒙LiteOS RK2206 LwIP Raw API 实现无阻塞UDP双向通信

鸿蒙LiteOS RK2206 LwIP Raw API 实现无阻塞UDP双向通信

B站 配套视频教程【鸿蒙 LiteOS 实战 14】LwIP Raw API实现全自动端口分配+无阻塞双向收发

一、前言

在鸿蒙LiteOS嵌入式物联网项目开发中,UDP通信是设备数据上报、远程指令控制最常用的通信方式。
日常开发中多数开发者习惯使用标准Socket接口实现UDP收发,但Socket模式存在线程阻塞、收发相互干扰、资源占用偏高等问题。

本文基于RK2206开发板 + 鸿蒙LiteOS,使用LwIP底层Raw原生API开发UDP客户端,采用异步回调接收+独立线程发送架构,彻底消除阻塞问题,实现无干扰双向通信,同时支持系统自动分配本地端口,无需手动指定端口号,适配性更强,是嵌入式高性能网络开发标准方案。

主要解决问题

  1. 如果解决阻塞的问题
  2. 如果做到同时收和发
  3. raw 和 socket有什么区别

二、传统Socket UDP存在的两大痛点

1. 阻塞等待问题

Socket提供的recvfrom属于阻塞式接收函数,在没有服务器数据下发时,当前线程会直接挂起休眠等待,无法执行其他业务逻辑,发送周期被接收状态严重影响,实时性大打折扣。

2. 双向并发收发困难

若想要同时收发数据,必须拆分多线程运行,由于多个线程共用同一个Socket文件描述符,属于共享临界资源,必须添加互斥锁进行保护,不仅增加代码复杂度,还容易出现资源竞争、程序异常崩溃等问题。

三、Raw API核心优势与解决思路

  1. 摒弃阻塞读取:采用LwIP内核异步回调机制,数据到达自动触发接收函数,程序无需主动轮询等待
  2. 收发逻辑解耦:接收交由内核回调处理,发送独立线程定时执行,两者互不干扰
  3. 无需线程锁:架构天然无共享资源竞争,省去互斥锁创建、加锁、解锁流程
  4. 端口自动分配:绑定端口传入0,由LwIP协议栈自动分配空闲本地端口,避免端口占用冲突
  5. 底层高性能:直接调用LwIP原生接口,跳过Socket封装层,运行效率更高、内存占用更小

四、实现架构:真正做到同时收发

  1. 数据接收端:LwIP内核层回调函数,网络数据包抵达网卡后,协议栈自动调用注册好的接收回调函数完成数据解析,不占用业务线程
  2. 数据发送端:独立LiteOS任务线程,按照自定义周期定时向上位机服务器发送设备数据
  3. 网络前置条件:程序上电自动等待WiFi连接成功并获取有效IP地址,联网成功后再初始化UDP网络

整体架构完全并行运行,接收不打断发送,发送不影响接收,实现标准意义上的全双工无阻塞UDP通信

五、关键技术点详解

5.1 彻底解决线程阻塞

传统方案:任务主动调用接收函数 → 无数据则阻塞休眠
Raw方案:内核被动推送数据 → 有数据才执行接收逻辑,空闲状态线程全程正常运行

5.2 端口自动分配原理

调用udp_bind绑定本地端口时,第二个端口参数填写0,LwIP协议栈会自动从系统空闲端口池中选取未被占用的端口完成绑定,绑定成功后可直接从UDP控制块local_port成员读取实际分配端口。

// 自动分配本地端口udp_bind(g_udp_pcb,IP_ADDR_ANY,0);// 获取分配完成的端口号g_local_port=g_udp_pcb->local_port;

5.3 LwIP数据缓冲区pbuf使用

LwIP所有网络数据收发都依赖pbuf数据包缓冲区,发送前申请内存存放数据,发送完成必须手动调用pbuf_free释放内存,避免出现内存泄漏。

六、完整可运行源码

/* * Copyright (c) 2022 FuZhou Lockzhiner Electronic Co., Ltd. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * http://www.apache.org/licenses/LICENSE-2.0 */#include"ohos_init.h"#include"los_task.h"#include"lz_hardware.h"#include"config_network.h"#include"lwip/udp.h"#include"lwip/ip_addr.h"#include"lwip/pbuf.h"#include<string.h>#include<stdio.h>#defineLOG_TAG"udp_raw"#defineOC_SERVER_IP"192.168.111.61"// 上位机服务器IP#defineSERVER_PORT20108// 上位机服务器端口#defineSEND_INTERVAL_MS1000// 数据发送间隔staticstructudp_pcb*g_udp_pcb=NULL;staticip4_addr_tg_server_ip;staticu16_tg_local_port=0;// 存储系统自动分配的本地端口staticunsignedintg_send_cnt=0;// 发送数据计数/** * @brief 等待WiFi连接并获取本机IP地址 * @param info WiFi信息结构体 * @return 0连接成功,-1连接失败 */intudp_get_wifi_info(WifiLinkedInfo*info){intret=-1;memset(info,0,sizeof(WifiLinkedInfo));unsignedintretry=20;while(retry--){if(GetLinkedInfo(info)==WIFI_SUCCESS){if(info->connState==WIFI_CONNECTED&&info->ipAddress!=0){LZ_HARDWARE_LOGD(LOG_TAG,"WiFi IP: %s",inet_ntoa(info->ipAddress));ret=0;break;}}LOS_Msleep(1000);}returnret;}/** * @brief UDP数据异步接收回调函数 * @param arg 自定义传入参数 * @param pcb UDP控制块 * @param p 网络数据缓冲区 * @param addr 发送方IP地址 * @param port 发送方端口号 */staticvoidudp_recv_callback(void*arg,structudp_pcb*pcb,structpbuf*p,constip_addr_t*addr,u16_tport){if(p==NULL)return;printf("[Raw Recv] %.*s\n",p->len,(char*)p->payload);pbuf_free(p);// 释放pbuf内存,防止内存泄漏}/** * @brief Raw API UDP数据发送函数 */voidudp_raw_send(void){charbuf[64];snprintf(buf,sizeof(buf),"RK2206 Raw API UDP msg: %u \r\n",g_send_cnt++);// 申请传输层pbuf缓冲区structpbuf*p=pbuf_alloc(PBUF_TRANSPORT,strlen(buf),PBUF_RAM);if(p==NULL)return;// 拷贝发送数据memcpy(p->payload,buf,strlen(buf));// 指定IP与端口发送数据udp_sendto(g_udp_pcb,p,&g_server_ip,SERVER_PORT);printf("[Raw Send] %s\n",buf);pbuf_free(p);}/** * @brief 独立发送任务线程 */voidudp_send_thread(void){while(1){if(g_udp_pcb!=NULL){udp_raw_send();}LOS_Msleep(SEND_INTERVAL_MS);}}/** * @brief UDP Raw API初始化 * @note 绑定端口填写0,实现系统自动分配本地端口 */voidudp_raw_init(void){// 创建UDP协议控制块g_udp_pcb=udp_new();if(g_udp_pcb==NULL){printf("udp_new failed!\n");return;}// 绑定任意网卡,端口0自动分配err_terr=udp_bind(g_udp_pcb,IP_ADDR_ANY,0);if(err!=ERR_OK){printf("udp_bind failed!\n");return;}// 获取协议栈自动分配的本地端口g_local_port=g_udp_pcb->local_port;// 注册异步接收回调函数udp_recv(g_udp_pcb,udp_recv_callback,NULL);// 配置服务器IP地址IP4_ADDR(&g_server_ip,192,168,111,61);printf("===== UDP Raw API 初始化完成 =====\n");printf("本地端口(系统自动分配): %d\n",g_local_port);printf("目标服务器地址: %s:%d\n",OC_SERVER_IP,SERVER_PORT);}/** * @brief UDP网络业务主流程 */voidudp_raw_example_process(void){WifiLinkedInfo info;// 循环等待WiFi联网成功while(udp_get_wifi_info(&info)!=0){LOS_Msleep(500);}// 初始化LwIP Raw UDPudp_raw_init();// 创建独立发送任务unsignedintsend_tid;TSK_INIT_PARAM_S send_task={0};send_task.pfnTaskEntry=(TSK_ENTRY_FUNC)udp_send_thread;send_task.uwStackSize=8192;send_task.pcName="udp_send_task";send_task.usTaskPrio=24;LOS_TaskCreate(&send_tid,&send_task);}/** * @brief 系统开机自启动入口 */voidudp_client_raw_example(void){unsignedintthread_id;TSK_INIT_PARAM_S task={0};task.pfnTaskEntry=(TSK_ENTRY_FUNC)udp_raw_example_process;task.uwStackSize=10240;task.pcName="udp_raw_main";task.usTaskPrio=23;LOS_TaskCreate(&thread_id,&task);}// 鸿蒙系统应用自动初始化APP_FEATURE_INIT(udp_client_raw_example);

七、程序运行效果

  1. 设备上电自动连接WiFi,打印本机局域网IP
  2. 联网成功后初始化UDP协议栈,打印系统自动分配的本地端口
  3. 每秒主动向服务器推送自定义设备消息
  4. 上位机下发指令可实时被回调函数捕获打印
WiFi IP: 192.168.111.105 ===== UDP Raw API 初始化完成 ===== 本地端口(系统自动分配): 52010 目标服务器地址: 192.168.111.61:20108 [Raw Send] RK2206 Raw API UDP msg: 0 [Raw Send] RK2206 Raw API UDP msg: 1 [Raw Recv] Server test data [Raw Send] RK2206 Raw API UDP msg: 2

八、LwIP Raw API 与 Socket API详细对比

对比维度标准Socket APILwIP Raw原生API
接收模式主动调用函数阻塞等待内核异步回调被动接收
阻塞特性存在阻塞,影响业务时序全程无阻塞,线程运行流畅
并发收发多线程需互斥锁保护天然线程安全,无需加锁
调用层级LwIP上层封装接口直接调用协议栈底层接口
运行性能中等,存在封装损耗性能最优,资源占用极低
端口使用手动指定固定端口支持填0自动分配空闲端口
开发难度入门简单,上手快熟悉协议栈后开发更灵活
适用场景快速调试、简易通信项目工业物联网、高并发、低功耗设备

九、开发总结

  1. 采用LwIP Raw异步回调彻底解决传统UDP接收阻塞问题,保障设备业务逻辑稳定运行
  2. 发送任务+内核回调的分离架构,轻松实现UDP全双工同时收发,互不干扰
  3. 绑定端口置0实现协议栈自动分配端口,有效解决多设备同网段端口冲突问题
  4. Raw API跳过Socket封装层,更贴合嵌入式底层开发需求,在低配置IoT芯片中优势明显
  5. 开发使用pbuf缓冲区务必及时释放,长期运行项目必须做好内存管理,避免内存泄漏

本文代码完全适配RK2206鸿蒙LiteOS原生工程,修改服务器IP与端口即可直接编译烧录,可直接用于物联网数据采集、无线遥控、局域网设备通信等实际项目开发。

十、思考

  • 如果客户端不给服务端发数据,服务端能发送数据给客户端吗?
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/9 16:37:37

实战指南:如何用自动化系统构建你的Steam饰品交易监控平台

实战指南&#xff1a;如何用自动化系统构建你的Steam饰品交易监控平台 【免费下载链接】SteamTradingSiteTracker Steam 挂刀行情站 —— 24小时更新的 BUFF & IGXE & C5 & UUYP & ECO 挂刀比例数据 | Track cheap Steam Community Market items on buff.163.c…

作者头像 李华
网站建设 2026/6/9 16:31:55

MCU硬件设计入门:从引脚复用与封装选型到PCB布局实战

1. 项目概述&#xff1a;从引脚与封装开始你的硬件设计当你拿到一颗全新的微控制器&#xff08;MCU&#xff09;&#xff0c;比如恩智浦&#xff08;NXP&#xff09;的Kinetis KL27系列&#xff0c;准备开始设计一块电路板时&#xff0c;第一件也是最关键的事情是什么&#xff…

作者头像 李华
网站建设 2026/6/9 16:31:53

解码器模型在序列标注任务中的优化策略

1. 序列标注任务与解码器模型的适配挑战序列标注&#xff08;Sequence Labeling, SL&#xff09;是自然语言处理中的基础任务&#xff0c;需要为文本序列中的每个token分配特定标签。典型应用包括&#xff1a;命名实体识别&#xff08;NER&#xff09;&#xff1a;识别文本中的…

作者头像 李华
网站建设 2026/6/9 16:30:09

yysScript阴阳师脚本:解放双手的智能挂机终极指南

yysScript阴阳师脚本&#xff1a;解放双手的智能挂机终极指南 【免费下载链接】yysScript 阴阳师脚本 支持御魂副本 双开 项目地址: https://gitcode.com/gh_mirrors/yy/yysScript 还在为阴阳师重复刷御魂副本而烦恼吗&#xff1f;每天花费数小时机械点击&#xff0c;不…

作者头像 李华