鸿蒙之接收UDP消息

开发环境

  1. VS Code
  2. HUAWEI DevEco Device Tool(HarmonyOS面向智能设备开发者提供的一站式集成开发环境)
  3. HiBurn(用于烧录)
  4. VMware
  5. Ubuntu(Linux系统)

使用的技术

UDP

Internet 协议集支持一个无连接的传输协议,该协议称为用户数据报协议(UDP,User Datagram Protocol)。UDP 为应用程序提供了一种无需建立连接就可以发送封装的 IP 数据包的方法。

Internet 的传输层有两个主要协议,互为补充。无连接的是 UDP,它除了给应用程序发送数据包功能并允许它们在所需的层次上架构自己的协议之外,几乎没有做什么特别的事情。面向连接的是 TCP,该协议几乎做了所有的事情。

UDP协议与TCP协议一样用于处理数据包,在OSI模型中,两者都位于传输层,处于IP协议的上一层。UDP有不提供数据包分组、组装和不能对数据包进行排序的缺点,也就是说,当报文发送之后,是无法得知其是否安全完整到达的。UDP用来支持那些需要在计算机之间传输数据的网络应用。包括网络视频会议系统在内的众多的客户/服务器模式的网络应用都需要使用UDP协议。UDP协议从问世至今已经被使用了很多年,虽然其最初的光彩已经被一些类似协议所掩盖,但即使在今天UDP仍然不失为一项非常实用和可行的网络传输层协议。

方法 说明

Close 关闭 UDP 连接

Connect 建立与远程主机的连接

DropMulticastGroup 退出多路广播组

JoinMulticastGroup 将 UdpClient 添加到多路广播组

Receive 返回已由远程主机发送的 UDP 数据文报

Send 将 UDP 数据文报发送到远程主机

方法

说明

Close

关闭 UDP 连接

Connect

建立与远程主机的连接

DropMulticastGroup

退出多路广播组

JoinMulticastGroup

将 UdpClient 添加到多路广播组

Receive

返回已由远程主机发送的 UDP 数据文报

Send

将 UDP 数据文报发送到远程主机

JSON

JSON介绍

JSON(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式。它基于 ECMAScript (欧洲计算机协会制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。

JSON 语法规则

JSON是一个标记符的序列。这套标记符包含六个构造字符、字符串、数字和三个字面名。

JSON是一个序列化的对象或数组。

  1. 六个构造字符:
    begin-array = ws %x5B ws ; [ 左方括号
    begin-object = ws %x7B ws ; { 左大括号
    end-array = ws %x5D ws ; ] 右方括号
    end-object = ws %x7D ws ; } 右大括号
    name-separator = ws %x3A ws ; : 冒号
    value-separator = ws %x2C ws ; , 逗号
  2. 在这六个构造字符的前或后允许存在无意义的空白符(ws):
    ws = *(%x20 /; 空间
    %x09 /; 水平标签
    %x0A /; 换行或换行
    %x0D); 回程
  3. JSON的值
    JSON的构成: ws 值 ws [1]
    值可以是对象、数组、数字、字符串或者三个字面值(false、null、true)中的一个。值中的字面值中的英文必须使用小写。
    对象由花括号括起来的逗号分割的成员构成,成员是字符串键和上文所述的值由逗号分割的键值对组成,如:
    1

{“name”: “John Doe”, “age”: 18, “address”: {“country” : “china”, “zip-code”: “10000”}}

数组是由方括号括起来的一组值构成,如:

[3, 1, 4, 1, 5, 9, 2, 6]

字符串与C或者Java的字符串非常相似。字符串是由双引号包围的任意数量Unicode字符的集合,使用反斜线转义。一个字符(character)即一个单独的字符串(character string)。

数字也与C或者Java的数值非常相似。除去未曾使用的八进制与十六进制格式。除去一些编码细节。

一些合法的JSON的实例:

{"a": 1, "b": [1, 2, 3]}
[1, 2, "3", {"a": 4}]
3.14
"wulianwangzhishi"

具体设计

文件结构

先在./applications/sample/wifi-iot/app路径下新建一个目录(或一套目录结构),用于存放业务源码文件。

本例程:在app下新增业务led,其中hello_world.c为业务代码,BUILD.gn为编译脚本,具体规划目录结构如下:

└── applications
└── sample
└── wifi-iot
└── app
│── led
│── led.c
└── BUILD.gn
└── BUILD.gn

功能实现

新建./applications/sample/wifi-iot/app/led下的led.c文件,在led.c中新建业务入口函数led,并实现业务逻辑。并在代码最下方,使用HarmonyOS启动恢复模块接口SYS_RUN()启动业务。(SYS_RUN定义在ohos_init.h文件中)

BUILD.gn为编译脚本

led.c为业务逻辑代码所在文件

BUILD.gn(app/led/BUILD.gn)

static_library("bahuyang") {
sources = [
"led.c"
]

include_dirs = [
"//utils/native/lite/include",
"//kernel/liteos_m/components/cmsis/2.0",
"//base/iot_hardware/interfaces/kits/wifiiot_lite",
]
}
  1. “bahuyang”:是生成静态库名称,可随意更改
  2. “led.c”:代码文件

BUILD.gn(app\BUILD.gn)

import("//build/lite/config/component/lite_component.gni")

lite_component("app") {
features = [
#"startup",
"led:bahuyang"
]
}
  1. 将"startup"注释,运行我们自己的文件
  2. “led”:工程目录
  3. bahuyang:静态库文件

搭建的模块

  1. WiFi模块
  2. OLED显示屏
  3. 红绿黄三色交通灯

主要代码

sta联网

#define APP_INIT_VAP_NUM    2
#define APP_INIT_USR_NUM 2
volatile char start_wifi_connected_flg = 0;
static struct netif *g_lwip_netif = NULL;
void hi_sta_reset_addr(struct netif *pst_lwip_netif)
{
ip4_addr_t st_gw;
ip4_addr_t st_ipaddr;
ip4_addr_t st_netmask;
printf("%s %d \r\n", __FILE__, __LINE__);
if (pst_lwip_netif == NULL) {
printf("hisi_reset_addr::Null param of netdev\r\n");
return;
}
IP4_ADDR(&st_gw, 0, 0, 0, 0);
IP4_ADDR(&st_ipaddr, 0, 0, 0, 0);
IP4_ADDR(&st_netmask, 0, 0, 0, 0);

netifapi_netif_set_addr(pst_lwip_netif, &st_ipaddr, &st_netmask, &st_gw);
}

void wifi_wpa_event_cb(const hi_wifi_event *hisi_event)
{
if (hisi_event == NULL)
return;

switch (hisi_event->event) {
case HI_WIFI_EVT_SCAN_DONE:
printf("WiFi: Scan results available\n");
break;
case HI_WIFI_EVT_CONNECTED:
printf("WiFi: Connected\n");
netifapi_dhcp_start(g_lwip_netif);

start_wifi_connected_flg = 1;

break;
case HI_WIFI_EVT_DISCONNECTED:
printf("WiFi: Disconnected\n");
netifapi_dhcp_stop(g_lwip_netif);
hi_sta_reset_addr(g_lwip_netif);
break;
case HI_WIFI_EVT_WPS_TIMEOUT:
printf("WiFi: wps is timeout\n");
break;
default:
break;
}
}

int hi_wifi_start_connect(void)
{
int ret;
errno_t rc;
hi_wifi_assoc_request assoc_req = {0};

/* copy SSID to assoc_req */
//热点名称
rc = memcpy_s(assoc_req.ssid, HI_WIFI_MAX_SSID_LEN + 1, "BAHUYANG", 8); /* 9:ssid length */
if (rc != EOK) {
printf("%s %d \r\n", __FILE__, __LINE__);
return -1;
}

/*
* OPEN mode
* for WPA2-PSK mode:
* set assoc_req.auth as HI_WIFI_SECURITY_WPA2PSK,
* then memcpy(assoc_req.key, "12345678", 8).
*/
//热点加密方式
assoc_req.auth = HI_WIFI_SECURITY_WPA2PSK;

/* 热点密码 */
memcpy(assoc_req.key, "123456789", 9);


ret = hi_wifi_sta_connect(&assoc_req);
if (ret != HISI_OK) {
printf("%s %d \r\n", __FILE__, __LINE__);
return -1;
}
printf("%s %d \r\n", __FILE__, __LINE__);
return 0;
}

int hi_wifi_start_sta(void)
{
int ret;
char ifname[WIFI_IFNAME_MAX_SIZE + 1] = {0};
int len = sizeof(ifname);
const unsigned char wifi_vap_res_num = APP_INIT_VAP_NUM;
const unsigned char wifi_user_res_num = APP_INIT_USR_NUM;

printf("%s %d \r\n", __FILE__, __LINE__);

ret = hi_wifi_init(wifi_vap_res_num, wifi_user_res_num);
if (ret != HISI_OK) {
printf("%s %d \r\n", __FILE__, __LINE__);
//return -1;
}

printf("%s %d \r\n", __FILE__, __LINE__);
ret = hi_wifi_sta_start(ifname, &len);
if (ret != HISI_OK) {
printf("%s %d \r\n", __FILE__, __LINE__);
return -1;
}

/* register call back function to receive wifi event, etc scan results event,
* connected event, disconnected event.
*/
ret = hi_wifi_register_event_callback(wifi_wpa_event_cb);
if (ret != HISI_OK) {
printf("register wifi event callback failed\n");
}

/* acquire netif for IP operation */
g_lwip_netif = netifapi_netif_find(ifname);
if (g_lwip_netif == NULL) {
printf("%s: get netif failed\n", __FUNCTION__);
return -1;
}

/* if received scan results, select one SSID to connect */
ret = hi_wifi_start_connect();
if (ret != 0) {
printf("%s %d \r\n", __FILE__, __LINE__);
return -1;
}


return 0;
}

void hi_wifi_stop_sta(void)
{
int ret;

ret = hi_wifi_sta_stop();
if (ret != HISI_OK) {
printf("failed to stop sta\n");
}

ret = hi_wifi_deinit();
if (ret != HISI_OK) {
printf("failed to deinit wifi\n");
}

g_lwip_netif = NULL;


}



void mqtt_test_thread(void * argv)
{

argv = argv;

hi_wifi_start_sta();

while(start_wifi_connected_flg == 0)
{
usleep(300000);
}

sleep(3);
LED_test();

}


void led_off(void)
{
GpioSetOutputVal(WIFI_IOT_IO_NAME_GPIO_9, 1);
}

void led_on(void)
{
GpioSetOutputVal(WIFI_IOT_IO_NAME_GPIO_9, 0);
}


void StaExampleEntry(void)
{
osThreadAttr_t attr;

attr.name = "wifi_config_thread";
attr.attr_bits = 0U;
attr.cb_mem = NULL;
attr.cb_size = 0U;
attr.stack_mem = NULL;
attr.stack_size = 4096;
attr.priority = 36;

if (osThreadNew((osThreadFunc_t)mqtt_test_thread, NULL, &attr) == NULL) {
printf("[LedExample] Falied to create LedTask!\n");
}
}

SYS_RUN(StaExampleEntry);

接收UDP数据

程序流程如下:

  1. 创建一个UDP socket句柄,以及一个变量toAd的人,并设置服务器的IP地址和端口号
  2. 使用sendto()函数向服务器发送数据
  3. 使用recvfrom()函数从服务器接受消息
  4. 使用close()函数关闭此socket
char recvline[1024];
void udp_thread(void *pdata)
{
int ret;
struct sockaddr_in servaddr;
cJSON *recvjson;
pdata = pdata;
int sockfd = socket(PF_INET, SOCK_DGRAM, 0);
//服务器 ip port
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(50001);
printf("udp_thread \r\n");
bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
while(1)
{
struct sockaddr_in addrClient;
int sizeClientAddr = sizeof(struct sockaddr_in);
memset(recvline, sizeof(recvline), 0);
ret = recvfrom(sockfd, recvline, 1024, 0, (struct sockaddr*)&addrClient,(socklen_t*)&sizeClientAddr);
if(ret>0)
{
char *pClientIP =inet_ntoa(addrClient.sin_addr);
printf("%s-%d(%d) says:%s\n",pClientIP,ntohs(addrClient.sin_port),addrClient.sin_port, recvline);
//进行json解析
recvjson = cJSON_Parse(recvline);
if(recvjson != NULL)
{
if(cJSON_GetObjectItem(recvjson, "cmd")->valuestring != NULL)
{
printf("cmd : %s\r\n", cJSON_GetObjectItem(recvjson, "cmd")->valuestring);
if(strcmp("RED", cJSON_GetObjectItem(recvjson, "cmd")->valuestring) == 0)
{
set_LED_status(LED_STATUS_RED);
printf("RED\r\n");
}
if(strcmp("YELLOW", cJSON_GetObjectItem(recvjson, "cmd")->valuestring) == 0)
{
set_LED_status(LED_STATUS_YELLOW);
printf("YELLOW\r\n");
}
if(strcmp("GREEN", cJSON_GetObjectItem(recvjson, "cmd")->valuestring) == 0)
{
set_LED_status(LED_STATUS_GREEN);
printf("GREEN\r\n");
}
if(strcmp("CLOSE", cJSON_GetObjectItem(recvjson, "cmd")->valuestring) == 0)
{
set_LED_status(LED_STATUS_CLOSE);
printf("CLOSE\r\n");
}
}
cJSON_Delete(recvjson);
}
}
}
}
void start_udp_thread(void)
{
osThreadAttr_t attr;
attr.name = "wifi_config_thread";
attr.attr_bits = 0U;
attr.cb_mem = NULL;
attr.cb_size = 0U;
attr.stack_mem = NULL;
attr.stack_size = 2048;
attr.priority = 36;
if (osThreadNew((osThreadFunc_t)udp_thread, NULL, &attr) == NULL) {
printf("[LedExample] Falied to create LedTask!\n");
}
}

后续

更多项目,可以关注我的程序设计专栏。

关注微信公众号

鸿蒙之接收UDP消息_数据

编写不易,感谢支持。