【鸿蒙开发】Hi3861学习笔记- UDP客户端
00. 目录
文章目录
- 00. 目录
- 01. UDP概述
- 02. UDP特点
- 03. UDP应用场景
- 04. UDP相关API
- 05. UDP编程流程
- 06. 硬件设计
- 07. 软件设计
- 08. 实验现象
- 09. 附录
01. UDP概述
UDP 通信是一种基于用户数据报协议(User Datagram Protocol, UDP)的网络通信方式。UDP 是一种无连接的、不可靠的、面向数据
报的传输层协议,它主要用于那些对实时性要求较高,但对数据准确性要求不是非常严格的场景。
02. UDP特点
● 无连接:UDP 在发送数据之前不需要与对方建立连接,每个数据报都是一个独立的信息传输单元。
● 不可靠传输:UDP 不保证数据的可靠传输,它只负责将数据报发送出去,不保证对方是否接收到数据,也不进行数据重传。
● 面向数据报:UDP 协议是面向数据报的,即发送端发送的每个数据报都是一个独立的单元,接收端也是以数据报为单位进行接收。
● 传输效率高:由于 UDP 不需要建立连接和进行复杂的可靠性控制,因此其传输效率比 TCP 高。
● 支持多对多通信:UDP 支持一对一、一对多、多对一和多对多的交互通信。
03. UDP应用场景
实时通信
视频会议(如Zoom)、在线游戏(如UDP+QUIC)、语音通话(如VoIP)。
简单查询/响应
DNS查询、DHCP动态IP分配、SNMP网络监控。
流媒体与广播
实时音视频流(如RTP协议)、IoT传感器数据上报。
低延迟场景
金融交易、QUIC协议(HTTP/3基础)等需要快速响应的场景。
常见基于UDP的协议
- DNS(域名解析)
- DHCP(动态主机配置)
- SNMP(网络管理)
- RTP/RTCP(实时传输)
- QUIC(HTTP/3底层协议)
04. UDP相关API
vendor\hisi\hi3861\hi3861\third_party\lwip_sack\include\sockets.h
-
socket()
-
bind()
-
accept()
-
shutdown()
-
getpeername()
-
getsockopt()
&setsockopt()
-
close()
-
read()
,readv()
,write()
,writev()
-
recv()
-
send()
,sendmsg()
,sendto()
-
select()
-
fcntl()
05. UDP编程流程
5.1 新建socket
在网络编程中所需要进行的第一件事情就是创建一个socket,无论是客户端还是服务器端,都需要创建一个socket,该函数返回socket文件描述符,类似于文件描述符。socket是一个结构体,被创建在内核中。
//在sock_fd 进行监听,在 new_fd 接收新的链接
int sock_fd;
//创建socket
if((sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
{
perror("create socket failed!\r\n");
exit(1);
}
5.2 配置将要连接的服务器信息(端口和IP)
按道理服务器的地址选择
255.255.255.255
,意思是不指定局域网内的某一设备,局域网所有的设备如果监听了这个端口号,那么都可以收到hi3861发来的消息。但是实际好像就只能填UDP服务器的地址,而已只能发送,不能接收。
#define UDP_PROT 8888
int addr_length;
//服务器的地址信息
struct sockaddr_in send_addr;
socklen_t addr_length = sizeof(send_addr);
//初始化预连接的服务端地址
send_addr.sin_family = AF_INET;
send_addr.sin_port = htons(UDP_PROT);
send_addr.sin_addr.s_addr = inet_addr("192.168.31.225");
addr_length = sizeof(send_addr);
5.3 发送数据
sendto()
用来将数据由指定的socket传给对方主机。参数s为已建好连线的socket。参数msg指向欲连线的数据内容,参数flags一般设0。
static const char *send_data = "Hello! I'm BearPi-HM_Nano UDP Client!\r\n";
//发送数据到服务远端
sendto(sock_fd, send_data, strlen(send_data), 0, (struct sockaddr *)&send_addr, addr_length);
5.4 接收数据
char recvBuf[512];
while (1)
{
bzero(recvBuf, sizeof(recvBuf));
...
//线程休眠一段时间
sleep(10);
//接收服务端返回的字符串
recvfrom(sock_fd, recvBuf, sizeof(recvBuf), 0, (struct sockaddr *)&send_addr, &addr_length);
printf("%s:%d=>%s\n", inet_ntoa(send_addr.sin_addr), ntohs(send_addr.sin_port), recvBuf);
}
06. 硬件设计
由于 Hi3861 内置 WIFI 功能,所以直接在开发板上使用即可,无需额外连接。
07. 软件设计
bsp_wifi.h
#ifndef __BSP_WIFI_H__
#define __BSP_WIFI_H__
#include "cmsis_os2.h"
#include "hi_io.h"
#include "hi_gpio.h"
#include "wifi_error_code.h"
#include "wifi_device.h"
//函数声明
WifiErrorCode WiFi_createHotSpots(const char *ssid, const char *psk);
WifiErrorCode WiFi_connectHotspots(const char *ssid, const char *psk);
#endif /* __BSP_WIFI_H__ */
bsp_wifi.c
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include "wifi_device.h"
#include "wifi_hotspot.h"
#include "lwip/netifapi.h"
#include "lwip/netif.h"
#include "lwip/ip4_addr.h"
#include "lwip/api_shell.h"
#include "bsp_wifi.h"
//WIFI通道
#define WIFI_CHANNEL 5
#define DEF_TIMEOUT 15
#define SELECT_WLAN_PORT "wlan0"
//STA 连接状态结果
int g_ConnectState = 0;
struct netif *g_lwip_netif = NULL;
//----------------------------WIFI AP----------------------------------
/** Hotspot state change */
void OnHotspotStateChangedCallbak(int state)
{
printf("OnHotspotStateChangedCallbak: state is %d.\n", state);
if (WIFI_HOTSPOT_ACTIVE == state)
{
printf("wifi hotspot active\n");
}
else
{
printf("wifi hotspot noactive\n");
}
}
/** Station connected */
void OnHotspotStaJoinCallbak(StationInfo *info)
{
static char macAddr[32] = {0};
static unsigned char *mac = NULL;
if (NULL == info)
{
printf("OnHotspotStaJoinCallbak is NULL\n");
}
else
{
mac = info->macAddress;
snprintf(macAddr, sizeof(macAddr), "%02X:%02X:%02X:%02X:%02X:%02X",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
printf("OnHotspotStaJoinCallbak: mac: %s reason: %d\n", macAddr, info->disconnectedReason);
}
}
/** Station disconnected */
void OnHotspotStaLeaveCallbak(StationInfo *info)
{
static char macAddr[32] = {0};
static unsigned char *mac = NULL;
if (NULL == info)
{
printf("OnHotspotStaLeaveCallbak is NULL\n");
}
else
{
mac = info->macAddress;
snprintf(macAddr, sizeof(macAddr), "%02X:%02X:%02X:%02X:%02X:%02X",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
printf("OnHotspotStaLeaveCallbak: mac: %s reason: %d\n", macAddr, info->disconnectedReason);
}
}
//创建Wifi热点
WifiErrorCode WiFi_createHotSpots(const char *ssid, const char *psk)
{
WifiErrorCode ret;
static WifiEvent event;
static HotspotConfig config;
printf("Start Initialization of WiFI AP Mode\r\n");
//注册WIFI事件的回调函数
event.OnHotspotStaJoin = OnHotspotStaJoinCallbak;
event.OnHotspotStaLeave = OnHotspotStaLeaveCallbak;
event.OnHotspotStateChanged =OnHotspotStateChangedCallbak;
ret = RegisterWifiEvent(&event);
if (WIFI_SUCCESS != ret)
{
printf("RegisterWifiEvent failed....\n");
return -1;
}
printf("RegisterWifiEvent OK .....\n");
//设置热点
strcpy(config.ssid, ssid);
strcpy(config.preSharedKey, psk);
config.band = HOTSPOT_BAND_TYPE_2G;
config.channelNum = WIFI_CHANNEL;
config.securityType = WIFI_SEC_TYPE_PSK;
ret = SetHotspotConfig(&config);
if (WIFI_SUCCESS != ret)
{
printf("SetHotspotConfig failed....\n");
return -1;
}
printf("SetHotspotConfig OK....\n");
//启动WIFI AP模式
ret = EnableHotspot();
if (WIFI_SUCCESS != ret)
{
printf("EnableHotspot failed...\n");
return -1;
}
printf("EnableHotspot OK ....\n");
//检查热点模式是否使能
if (WIFI_HOTSPOT_ACTIVE != IsHotspotActive())
{
printf("IsHotspotActive failed....\n");
return -1;
}
printf("IsHotspotActive OK .....\n");
}
//----------------------------WIFI STA----------------------------------
/** Connection state change */
void staOnWifiConnectionChanged(int state, WifiLinkedInfo *info)
{
if (state > 0)
{
g_ConnectState = 1;
printf("staOnWifiConnectionChanged state: %d\n", state);
}
else
{
printf("staOnWifiConnectionChanged failed state: %d\n", state);
}
}
/** Scan state change */
void staOnWifiScanStateChanged(int state, int size)
{
printf("staOnWifiScanStateChanged state: %d size: %d\n", state, size);
}
/** Hotspot state change */
void staOnHotspotStateChanged(int state)
{
printf("staOnHotspotStateChanged state: %d\n", state);
}
/** Station connected */
void staOnHotspotStaJoin(StationInfo *info)
{
printf("staOnHotspotStaJoin STA Join AP\n");
}
/** Station disconnected */
void staOnHotspotStaLeave(StationInfo *info)
{
printf("staOnHotspotStaLeave..\n");
}
//STA模式 连接WIFI
WifiErrorCode WiFi_connectHotspots(const char *ssid, const char *psk)
{
WifiErrorCode ret;
static WifiEvent event;
static WifiDeviceConfig config;
int result;
int timeout;
printf("---------------WIFI STA Mode------------\n");
//1. 注册WIFI事件
event.OnHotspotStaJoin = staOnHotspotStaJoin;
event.OnHotspotStaLeave = staOnHotspotStaLeave;
event.OnHotspotStateChanged = staOnWifiScanStateChanged;
event.OnWifiConnectionChanged = staOnWifiConnectionChanged;
event.OnWifiScanStateChanged = staOnWifiScanStateChanged;
ret = RegisterWifiEvent(&event);
if (WIFI_SUCCESS != ret)
{
printf("RegisterWifiEvent failed....\n");
return -1;
}
else
{
printf("RegisterWifiEvent OK....\n");
}
//2. 使能WIFI
ret = EnableWifi();
if (WIFI_SUCCESS != ret)
{
printf("EnableWifi failed...\n");
return -1;
}
//3. 判断WIFI是否激活
if (WIFI_STA_ACTIVE != IsWifiActive())
{
printf("IsWifiActive is not actived..\n");
return -1;
}
//4. 配置连接热点信息
strcpy(config.ssid, ssid);
strcpy(config.preSharedKey, psk);
config.securityType = WIFI_SEC_TYPE_PSK;
ret = AddDeviceConfig(&config, &result);
if (WIFI_SUCCESS != ret)
{
printf("AddDeviceConfig failed....\n");
return -1;
}
//5. 连接到热点
if (WIFI_SUCCESS == ConnectTo(result))
{
printf("ConnectTo OK.....\n");
}
else
{
printf("ConnectTo failed....\n");
return -1;
}
//6. 等待连接结果
timeout = DEF_TIMEOUT;
while(timeout > 0)
{
sleep(1);
timeout--;
if (1 == g_ConnectState)
{
printf("Connect to %s OK ....\n", ssid);
break;
}
}
if (timeout <= 0)
{
printf("Connect to %s timeout.....\n", ssid);
return -1;
}
//7. 获取网络接口
g_lwip_netif = netifapi_netif_find(SELECT_WLAN_PORT);
//8. 启动DHCP
if (NULL != g_lwip_netif)
{
dhcp_start(g_lwip_netif);
printf("dhcp_start begin dhcp....\n");
}
//9. 等待DHCP
for (;;)
{
if (dhcp_is_bound(g_lwip_netif) == ERR_OK)
{
//打印获取到的IP信息
netifapi_netif_common(g_lwip_netif, dhcp_clients_info_show, NULL);
break;
}
printf("DHCP IP InProgress.....\n");
sleep(1);
}
//10. 执行其它操作
}
template.c
#include <stdio.h>
#include <unistd.h>
#include "ohos_init.h"
#include "cmsis_os2.h"
#include "bsp_wifi.h"
#include "lwip/sockets.h"
#define TASK_STACK_SIZE 1024
static const char *data = "hello shenzhen";
//任务1ID
osThreadId_t task1_id;
osThreadId_t task2_id;
//线程回调入口函数
void task1 (void *argument)
{
int sockfd = -1;
int ret = -1;
char buf[128];
struct sockaddr_in addr;
//连接到WIFI
WiFi_connectHotspots("IOT", "iot12345678");
//创建socket
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (-1 == sockfd)
{
printf("socket failed....\n");
return;
}
//初始化预连接的服务端地址
memset(&addr, 0, sizeof(addr));
addr.sin_addr.s_addr = inet_addr("192.168.16.51");
addr.sin_family = AF_INET;
addr.sin_port = htons(10086);
//循环接收
while(1)
{
//发送数据
ret = sendto(sockfd, data, strlen(data), 0, &addr, sizeof(addr));
if (-1 == ret)
{
printf("sendto error....\n");
break;
}
printf("sendto: %s ret: %d\n", data, ret);
sleep(1);
//接收数据
memset(buf, 0, sizeof(buf));
ret = recvfrom(sockfd, buf, sizeof(buf), 0, NULL, NULL);
if (-1 == ret)
{
printf("recvfrom failed...\n");
break;
}
printf("recvfrom: %s len: %d\n", buf, ret);
}
//关闭套接字
closesocket(sockfd);
}
/**
* @description: 初始化并创建任务
* @param {*}
* @return {*}
*/
static void template_demo(void)
{
osThreadAttr_t attr;
attr.name = "task1"; //任务名称
attr.attr_bits = osThreadDetached; //分离状态
attr.cb_mem = NULL;
attr.cb_size = 0;
attr.stack_mem = NULL;
attr.stack_size = TASK_STACK_SIZE * 10;
attr.priority = osPriorityNormal;
//创建任务1
task1_id = osThreadNew(task1, NULL, &attr);
if (NULL != task1_id)
{
printf("任务1创建OK task1_id = %d\n", task1_id);
}
}
SYS_RUN(template_demo);