[国产MCU]-W801开发实例-用户报文协议(UDP)数据接收和发送
用户报文协议(UDP)数据接收和发送
文章目录
- 用户报文协议(UDP)的数据接收与发送
- 第一章、UDP简要概述
- 第二章、W801系统中UDP创建的基本流程
- 第二章第1小节、关于UDP的操作步骤
- 第二章第1小节第1步:理解基本概念
- 第二章第1小节第2步:配置相关参数
- 第二章第2小节、代码实现方案
- 第二章第2小节第3步:编写核心代码模块
- 第二章第2小节第4步:调试与验证程序功能
- 第二章第1小节、关于UDP的操作步骤
- 第二章、W801系统中UDP创建的基本流程
- 第一章、UDP简要概述
1、UDP简单介绍
UDP协议(用户数据报协议)是一种跨越互联网的通信方式,在实时性要求较高的场景下被广泛应用。例如,在视频流媒体服务或网络 DNS查询任务中使用该协议能够显著提升传输效率。UDP通过避免预先建立连接的方式实现了更快的数据传递速度。
UDP 主要应用于对实时性要求高的通信场景,在这些场景中临时丢弃数据包比等待恢复更优。语音与视频流量采用该协议传输因其具有实时性特征且旨在容忍一定程度的数据丢失。例如,在互联网基础服务中许多在线通话系统采用 IP 语音(VOIP)技术运行于 UDP 协议这是因为相较于提供清晰通话但存在较大延迟的传统电话连接而言静态通话方案更为可取
UDP的通信过程如下:

UDP与TCP通信的区别如下:

TCP通信应接收和确认数据包按照预期到达;如果一个数据包未到达——例如由于中间网络拥塞——则需重新发送。UDP通信则不包含这些功能。
这些差异带来了显著的优势。由于 UDP 无需进行握手操作或确认数据完整性,并且能够直接传输数据包而不受可靠性约束的影响,在传输速度方面比 TCP 更加高效。
然而这样的传输速度必然导致问题。当UDP数据报在网络传输中出现丢失时无法自动重传因此为了确保可靠的数据传输UDP协议的使用必须具备容错能力以处理数据丢包断路以及数据重传的情况。
在技术层面而言,这种数据包丢失并非UDP系统中所固有的缺陷,而是源于互联网架构本身的特性.大部分网络路由器并未按照设计进行数据包排序及确认流程,这一做法因所需内存超出现有技术条件而无法实施.作为解决这一问题的技术方案,TCP机制在特定应用需求下发挥着重要作用.
本文将演示如何在W801中创建UDP通信。
2、W801的UDP创建逻辑
该SDK的底层架构基于lwip2.0网络堆栈库进行了构建。因此,在实现UDP功能时需严格遵守lwip2.0的规范要求。其中包含数据监听与消息发送两大核心流程。
2.1 UDP使用步骤
第一步,创建socket
socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
该套接字功能模块[链接]用于实现指定协议的套接字创建。本实例旨在实现UDP协议套接字的建立。
第二步,绑定IP和端口
int res = bind(socket_num,(struct sockaddr*)&socket_addr,sizeof(socket_addr));
bind函数将监听socket的指定端口。
第三步,接收数据
len = recvfrom(socket_num,sock_rx_buffer,UDP_CLIENT_SOCK_BUF_SIZE,
0,(struct sockaddr*)&pin,&addrlen);
recvfrom函数用于接收UDP数据。函数将返回接收数据的长度。
第四步,发送数据
ret = sendto(socket_num,sock_rx_buffer,len,0,
(struct sockaddr *)&pin, sizeof(struct sockaddr));
该函数通过指定地址和端口传输预定义数据量,并返回本次传输的数据量。
2.2 代码实现
在工程中添加udp_task.c文件
1)导入依赖库头文件
#include <stdio.h>
#include "wm_include.h"
#include "utils/delay.h"
#include "hardware/wifi.h"
#include <string.h>
#include "lwip/errno.h"
#include "udp_task.h"
在本节中所涉及的$hardware/wifi.h$包含了我们自行开发的WiFi连接封装函数头文件的内容,请参阅上一篇的内容。
- WiFi连接
2)定义UDP缓存、任务堆栈等变量
#define UDP_CLIENT_TASK_SIZE 512
#define UDP_CLIENT_SOCK_BUF_SIZE 128
// user prio 32 - 60
#define TCP_CLIENT_TASK_PRIO 32
static OS_STK __tcp_client_task_stack[UDP_CLIENT_TASK_SIZE];
char sock_rx_buffer[UDP_CLIENT_SOCK_BUF_SIZE] = {0};
#define SSID "TP-LINK_FE2A"
#define PWD "iot123456"
#define UDP_PORT 3000
int socket_num = -1; // Socket ID
3)定义UDP的Socket创建函数
void create_udp_socket(void){
struct sockaddr_in socket_addr;
struct tls_ethif *ethif;
// int ttl = 10;
//int loop = 0;
ethif = tls_netif_get_ethif();
// 创建UDP socket
socket_num = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
memset(&socket_addr, 0, sizeof(struct sockaddr_in));
socket_addr.sin_family = AF_INET;
socket_addr.sin_addr.s_addr = (u32)0x00000000UL;
// 0.0.0.0
socket_addr.sin_port = htons(UDP_PORT);
// 绑定
int res = bind(socket_num,(struct sockaddr*)&socket_addr,sizeof(socket_addr));
if(res == -1){
return WM_FAILED;
}
printf("created udp socket\r\n");
return WM_SUCCESS;
}
3)定义WiFi状态回调函数
// WiFi状态改变回调
static void wifi_status_changed_event(u8 status )
{
switch(status)
{
case NETIF_WIFI_JOIN_SUCCESS:
printf("\nNETIF_WIFI_JOIN_SUCCESS\n");
break;
case NETIF_WIFI_JOIN_FAILED:
printf("\nNETIF_WIFI_JOIN_FAILED\n");
break;
case NETIF_WIFI_DISCONNECTED:
printf("\nNETIF_WIFI_DISCONNECTED\n");
if(socket_num > 0){
closesocket(socket_num);
}
socket_num = -1;
break;
case NETIF_IP_NET_UP:
{
struct tls_ethif *tmpethif = tls_netif_get_ethif();
// 打印IP信息
print_ipaddr(&tmpethif->ip_addr);
#if TLS_CONFIG_IPV6
print_ipaddr(&tmpethif->ip6_addr[0]);
print_ipaddr(&tmpethif->ip6_addr[1]);
print_ipaddr(&tmpethif->ip6_addr[2]);
#endif
}
break;
default:
printf("UNKONWN STATE:%d\n", status);
break;
}
}
在wifi_status_changed_event该事件将响应WiFi状态变化并执行以下操作:捕获最新的无线信号信息以确定当前网络状态以及检测并记录断开连接的发生情况。当检测到WiFi断开连接时,则会关闭相关socket以释放网络资源。
4)创建UDP任务
void create_udp_task(void){
wifi_connect(SSID,PWD,wifi_status_changed_event);
while(1){
tls_os_time_delay(1);
struct tls_ethif * ethif = tls_netif_get_ethif();
if(ethif->status)
break;
}
printf("got netif\r\n");
create_udp_socket();
if(socket_num < 0){
printf("ceate and connect socket failed.\r\n");
while(1){
__delay_ms(1000);
}
}
int ret,len = 0;
struct sockaddr_in pin;
socklen_t addrlen = sizeof(struct sockaddr);
for(;;){
if(socket_num > 0){
memset(sock_rx_buffer,0,UDP_CLIENT_SOCK_BUF_SIZE);
len = recvfrom(socket_num,sock_rx_buffer,UDP_CLIENT_SOCK_BUF_SIZE,
0,(struct sockaddr*)&pin,&addrlen);
if(len > 0){
printf("udp recv from %s:port:%d len = %d\r\n",
inet_ntoa(pin.sin_addr),htons(pin.sin_port),len);
printf("data = %s\r\n",sock_rx_buffer);
ret = sendto(socket_num,sock_rx_buffer,len,0,
(struct sockaddr *)&pin, sizeof(struct sockaddr));
if(ret < 0){
printf("udp send failed(%d)\r\n",ret);
}else{
printf("udp send success:len = %d\r\n",len);
}
}
}
__delay_ms(10);
}
}
}
在create_udp_task函数中,我们首先连接WiFi:
wifi_connect(SSID,PWD,wifi_status_changed_event);
while(1){
tls_os_time_delay(1);
struct tls_ethif * ethif = tls_netif_get_ethif();
if(ethif->status)
break;
}
当WiFi连接成功后,调用create_udp_socket函数:
printf("got netif\r\n");
create_udp_socket();
if(socket_num < 0){
printf("ceate and connect socket failed.\r\n");
while(1){
__delay_ms(1000);
}
}
一旦socket连接建立失败,则程序将不会继续执行。
一旦socket连接建立成功后,则系统会开始监听并接收UDP数据。
int ret,len = 0;
struct sockaddr_in pin;
socklen_t addrlen = sizeof(struct sockaddr);
for(;;){
if(socket_num > 0){
memset(sock_rx_buffer,0,UDP_CLIENT_SOCK_BUF_SIZE);
len = recvfrom(socket_num,sock_rx_buffer,UDP_CLIENT_SOCK_BUF_SIZE,
0,(struct sockaddr*)&pin,&addrlen);
if(len > 0){
printf("udp recv from %s:port:%d len = %d\r\n",
inet_ntoa(pin.sin_addr),htons(pin.sin_port),len);
printf("data = %s\r\n",sock_rx_buffer);
ret = sendto(socket_num,sock_rx_buffer,len,0,
(struct sockaddr *)&pin, sizeof(struct sockaddr));
if(ret < 0){
printf("udp send failed(%d)\r\n",ret);
}else{
printf("udp send success:len = %d\r\n",len);
}
}
}
__delay_ms(10);
}
}
最后,启动任务
// app_start.h
#define __APP_H__
void app_start(void);
#endif
// udp_task.h
#ifndef __UDP_TASK_H__
#define __UDP_TASK_H__
void create_udp_task(void);
#endif
#include "app.h"
#include "tasks/udp_task.h"
void app_start(void){
create_udp_task();
}
void UserMain(void){
app_start();
}
