当前位置: 首页 > news >正文

Hi3861 OpenHarmony嵌入式应用入门--TCP Server

本篇使用的是lwip编写tcp服务端。需要提前准备好一个PARAM_HOTSPOT_SSID宏定义的热点,并且密码为PARAM_HOTSPOT_PSK

LwIP简介

LwIP是什么?

A Lightweight TCP/IP stack 一个轻量级的TCP/IP协议栈

详细介绍请参考LwIP项目官网:lwIP - A Lightweight TCP/IP stack - Summary [Savannah]

LwIP在openharmony上的应用情况

目前,openharmony源码树有两份LwIP:

  1. third_party/lwip
    • 源码形式编译
    • 供liteos-a内核使用
    • 还有一部分代码在kernel/liteos_a中,一起编译
  1. vendor/hisi/hi3861/hi3861/third_party/lwip_sack
    • hi3861-sdk的一部分
    • 静态库形式编译
    • 不可修改配置
    • 可以查看当前配置(vend

LwIP Socket API编程主要是6个步骤:

创建Tcp Server Socket:socket

绑定指定的IP和Port:bind

设置socket为监听状态:listen

阻塞方式等待client连接:accept

阻塞方式receive client的消息:recv

调用send发送消息给TCP Client

修改网络参数

在Hi3861开发板上运行上述四个测试程序之前,需要根据你的无线路由、Linux系统IP修改 net_params.h文件的相关代码:

  • PARAM_HOTSPOT_SSID 修改为你的热点名称
  • PARAM_HOTSPOT_PSK 修改为你的热点密码;

代码编写

修改D:\DevEcoProjects\test\src\vendor\rtplay\rt_hi3861\demo\BUILD.gn文件

# Copyright (c) 2023 Beijing HuaQing YuanJian Education Technology Co., Ltd
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
#    http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License. import("//build/lite/config/component/lite_component.gni")lite_component("demo") {features = [#"base_00_helloworld:base_helloworld_example",#"base_01_led:base_led_example",#"base_02_loopkey:base_loopkey_example",#"base_03_irqkey:base_irqkey_example",#"base_04_adc:base_adc_example",#"base_05_pwm:base_pwm_example",#"base_06_ssd1306:base_ssd1306_example",#"kernel_01_task:kernel_task_example",#"kernel_02_timer:kernel_timer_example",#"kernel_03_event:kernel_event_example",#"kernel_04_mutex:kernel_mutex_example",#"kernel_05_semaphore_as_mutex:kernel_semaphore_as_mutex_example",#"kernel_06_semaphore_for_sync:kernel_semaphore_for_sync_example",#"kernel_07_semaphore_for_count:kernel_semaphore_for_count_example",#"kernel_08_message_queue:kernel_message_queue_example",#"wifi_09_hotspot:wifi_hotspot_example",#"wifi_10_sta:wifi_sta_example","tcp_11_server:tcp_server_example",]
}

创建D:\DevEcoProjects\test\src\vendor\rtplay\rt_hi3861\demo\tcp_11_server文件夹

文件夹中创建D:\DevEcoProjects\test\src\vendor\rtplay\rt_hi3861\demo\tcp_11_server\BUILD.gn文件

#Copyright (C) 2021 HiHope Open Source Organization .
#Licensed under the Apache License, Version 2.0 (the "License");
#you may not use this file except in compliance with the License.
#You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
#Unless required by applicable law or agreed to in writing, software
#distributed under the License is distributed on an "AS IS" BASIS,
#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#See the License for the specific language governing permissions and
#
#limitations under the License.static_library("tcp_server_example") {# uncomment one of following line, to enable one test:sources = ["tcp_server_example.c"]sources += ["wifi_connecter.c"]include_dirs = ["//utils/native/lite/include","//kernel/liteos_m/kal","//foundation/communication/wifi_lite/interfaces/wifiservice",]
}

添加了wifi_connecter.c文件的编译,这个文件中有链接wifi的函数。

文件夹中创建D:\DevEcoProjects\test\src\vendor\rtplay\rt_hi3861\demo\tcp_11_server\net_common.h文件,文件主要引入一些头文件。

/** Copyright (C) 2021 HiHope Open Source Organization .* Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**     http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and** limitations under the License.*/#ifndef NET_COMMON_H
#define NET_COMMON_H// __arm__ and __aarch64__ for HarmonyOS with liteos-a kernel
// __i386__ and __x86_64__ for Unix like OS
#if defined(__arm__) || defined(__aarch64__) || defined(__i386__) || defined(__x86_64__)
#define HAVE_BSD_SOCKET 1
#else
#define HAVE_BSD_SOCKET 0
#endif#if defined(__riscv) // for wifiiot(HarmonyOS on Hi3861 with liteos-m kernel)
#define HAVE_LWIP_SOCKET 1
#else
#define HAVE_LWIP_SOCKET 0
#endif#if HAVE_BSD_SOCKET
#include <sys/types.h>  // for AF_INET SOCK_STREAM
#include <sys/socket.h> // for socket
#include <netinet/in.h> // for sockaddr_in
#include <arpa/inet.h> // for inet_pton
#elif HAVE_LWIP_SOCKET
#include "lwip/sockets.h"
#ifndef close
#define close(fd) lwip_close(fd)
#endif
#else
#error "Unknow platform!"
#endif#endif  // NET_COMMON_H

文件夹中创建D:\DevEcoProjects\test\src\vendor\rtplay\rt_hi3861\demo\tcp_11_server\wifi_connecter.h文件,该头文件包含wifi连接的宏。

/** Copyright (C) 2021 HiHope Open Source Organization .* Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**     http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and** limitations under the License.*/#ifndef WIFI_CONNECTER_H
#define WIFI_CONNECTER_H#include "wifi_device.h"#ifndef PARAM_HOTSPOT_SSID
#define PARAM_HOTSPOT_SSID "HarmonyOS"   // your AP SSID
#endif#ifndef PARAM_HOTSPOT_PSK
#define PARAM_HOTSPOT_PSK  "1234567890"  // your AP PSK
#endif#ifndef PARAM_HOTSPOT_TYPE
#define PARAM_HOTSPOT_TYPE WIFI_SEC_TYPE_PSK // defined in wifi_device_config.h
#endif#ifndef PARAM_SERVER_ADDR
#define PARAM_SERVER_ADDR "192.168.1.100" // your PC IP address
#endif#ifndef PARAM_SERVER_PORT
#define PARAM_SERVER_PORT 5678
#endifint ConnectToHotspot(WifiDeviceConfig* apConfig);void DisconnectWithHotspot(int netId);#endif  // WIFI_CONNECTER_H

文件夹中创建D:\DevEcoProjects\test\src\vendor\rtplay\rt_hi3861\demo\tcp_11_server\wifi_connecter.c文件,wifi连接的实现。

/** Copyright (C) 2021 HiHope Open Source Organization .* Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**     http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and** limitations under the License.*/#include "wifi_device.h"
#include "cmsis_os2.h"#include "lwip/netifapi.h"
#include "lwip/api_shell.h"#define IDX_0          0
#define IDX_1          1
#define IDX_2          2
#define IDX_3          3
#define IDX_4          4
#define IDX_5          5
#define DELAY_TICKS_10     (10)
#define DELAY_TICKS_100    (100)static void PrintLinkedInfo(WifiLinkedInfo* info)
{if (!info) return;static char macAddress[32] = {0};unsigned char* mac = info->bssid;snprintf(macAddress, sizeof(macAddress), "%02X:%02X:%02X:%02X:%02X:%02X",mac[IDX_0], mac[IDX_1], mac[IDX_2], mac[IDX_3], mac[IDX_4], mac[IDX_5]);printf("bssid: %s, rssi: %d, connState: %d, reason: %d, ssid: %s\r\n",macAddress, info->rssi, info->connState, info->disconnectedReason, info->ssid);
}static volatile int g_connected = 0;static void OnWifiConnectionChanged(int state, WifiLinkedInfo* info)
{if (!info) return;printf("%s %d, state = %d, info = \r\n", __FUNCTION__, __LINE__, state);PrintLinkedInfo(info);if (state == WIFI_STATE_AVALIABLE) {g_connected = 1;} else {g_connected = 0;}
}static void OnWifiScanStateChanged(int state, int size)
{printf("%s %d, state = %X, size = %d\r\n", __FUNCTION__, __LINE__, state, size);
}static WifiEvent g_defaultWifiEventListener = {.OnWifiConnectionChanged = OnWifiConnectionChanged,.OnWifiScanStateChanged = OnWifiScanStateChanged
};static struct netif* g_iface = NULL;err_t netifapi_set_hostname(struct netif *netif, char *hostname, u8_t namelen);int ConnectToHotspot(WifiDeviceConfig* apConfig)
{WifiErrorCode errCode;int netId = -1;errCode = RegisterWifiEvent(&g_defaultWifiEventListener);printf("RegisterWifiEvent: %d\r\n", errCode);errCode = EnableWifi();printf("EnableWifi: %d\r\n", errCode);errCode = AddDeviceConfig(apConfig, &netId);printf("AddDeviceConfig: %d\r\n", errCode);g_connected = 0;errCode = ConnectTo(netId);printf("ConnectTo(%d): %d\r\n", netId, errCode);while (!g_connected) { // wait until connect to APosDelay(DELAY_TICKS_10);}printf("g_connected: %d\r\n", g_connected);g_iface = netifapi_netif_find("wlan0");if (g_iface) {err_t ret = 0;char* hostname = "rtplay";ret = netifapi_set_hostname(g_iface, hostname, strlen(hostname));printf("netifapi_set_hostname: %d\r\n", ret);ret = netifapi_dhcp_start(g_iface);printf("netifapi_dhcp_start: %d\r\n", ret);osDelay(DELAY_TICKS_100); // wait DHCP server give me IP
#if 1ret = netifapi_netif_common(g_iface, dhcp_clients_info_show, NULL);printf("netifapi_netif_common: %d\r\n", ret);
#else// 下面这种方式也可以打印 IP、网关、子网掩码信息ip4_addr_t ip = {0};ip4_addr_t netmask = {0};ip4_addr_t gw = {0};ret = netifapi_netif_get_addr(g_iface, &ip, &netmask, &gw);if (ret == ERR_OK) {printf("ip = %s\r\n", ip4addr_ntoa(&ip));printf("netmask = %s\r\n", ip4addr_ntoa(&netmask));printf("gw = %s\r\n", ip4addr_ntoa(&gw));}printf("netifapi_netif_get_addr: %d\r\n", ret);
#endif}return netId;
}void DisconnectWithHotspot(int netId)
{if (g_iface) {err_t ret = netifapi_dhcp_stop(g_iface);printf("netifapi_dhcp_stop: %d\r\n", ret);}WifiErrorCode errCode = Disconnect(); // disconnect with your APprintf("Disconnect: %d\r\n", errCode);errCode = UnRegisterWifiEvent(&g_defaultWifiEventListener);printf("UnRegisterWifiEvent: %d\r\n", errCode);RemoveDevice(netId); // remove AP configprintf("RemoveDevice: %d\r\n", errCode);errCode = DisableWifi();printf("DisableWifi: %d\r\n", errCode);
}

文件夹中创建D:\DevEcoProjects\test\src\vendor\rtplay\rt_hi3861\demo\tcp_11_server\tcp_server_example.c文件

/** Copyright (C) 2021 HiHope Open Source Organization .* Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**     http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and** limitations under the License.*/#include <errno.h>
#include <stdio.h>
#include <string.h>
// #include <stddef.h>
#include <unistd.h>
#include "ohos_init.h"
#include "cmsis_os2.h"#include "net_common.h"
#include "wifi_connecter.h"#define DELAY_1S  (1)
#define STACK_SIZE         (10240)
#define DELAY_TICKS_10     (10)
#define DELAY_TICKS_100    (100)static char request[128] = "";
void TcpServerTest(void)
{WifiDeviceConfig config = {0};// 准备AP的配置参数// strcpy(config.ssid, PARAM_HOTSPOT_SSID);// strcpy(config.preSharedKey, PARAM_HOTSPOT_PSK);strcpy_s(config.ssid, WIFI_MAX_SSID_LEN, PARAM_HOTSPOT_SSID);strcpy_s(config.preSharedKey, WIFI_MAX_KEY_LEN, PARAM_HOTSPOT_PSK);config.securityType = PARAM_HOTSPOT_TYPE;osDelay(DELAY_TICKS_10);int netId = ConnectToHotspot(&config);ssize_t retval = 0;int backlog = 1;int sockfd = socket(AF_INET, SOCK_STREAM, 0); // TCP socketint connfd = -1;struct sockaddr_in clientAddr = {0};socklen_t clientAddrLen = sizeof(clientAddr);struct sockaddr_in serverAddr = {0};serverAddr.sin_family = AF_INET;serverAddr.sin_port = htons(PARAM_SERVER_PORT);  // 端口号,从主机字节序转为网络字节序serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); // 允许任意主机接入, 0.0.0.0retval = bind(sockfd, (struct sockaddr *)&serverAddr, sizeof(serverAddr)); // 绑定端口if (retval < 0) {printf("bind failed, %ld!\r\n", retval);goto do_cleanup;}printf("bind to port %hu success!\r\n", PARAM_SERVER_PORT);retval = listen(sockfd, backlog); // 开始监听if (retval < 0) {printf("listen failed!\r\n");goto do_cleanup;}printf("listen with %d backlog success!\r\n", backlog);// 接受客户端连接,成功会返回一个表示连接的 socket , clientAddr 参数将会携带客户端主机和端口信息 ;失败返回 -1// 此后的 收、发 都在 表示连接的 socket 上进行;之后 sockfd 依然可以继续接受其他客户端的连接,//  UNIX系统上经典的并发模型是“每个连接一个进程”——创建子进程处理连接,父进程继续接受其他客户端的连接//  鸿蒙liteos-a内核之上,可以使用UNIX的“每个连接一个进程”的并发模型//     liteos-m内核之上,可以使用“每个连接一个线程”的并发模型connfd = accept(sockfd, (struct sockaddr *)&clientAddr, &clientAddrLen);if (connfd < 0) {printf("accept failed, %d, %d\r\n", connfd, errno);goto do_cleanup;}printf("accept success, connfd = %d!\r\n", connfd);printf("client addr info: host = %s, port = %hu\r\n", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port));// 后续 收、发 都在 表示连接的 socket 上进行;retval = recv(connfd, request, sizeof(request), 0);if (retval < 0) {printf("recv request failed, %ld!\r\n", retval);goto do_disconnect;}printf("recv request{%s} from client done!\r\n", request);retval = send(connfd, request, strlen(request), 0);if (retval <= 0) {printf("send response failed, %ld!\r\n", retval);goto do_disconnect;}printf("send response{%s} to client done!\r\n", request);do_disconnect:sleep(DELAY_1S);close(connfd);sleep(DELAY_1S); // for debugdo_cleanup:printf("do_cleanup...\r\n");close(sockfd);DisconnectWithHotspot(netId);
}SYS_RUN(TcpServerTest);

使用build,编译成功后,使用upload进行烧录。

相关文章:

  • 什么是前端开发?
  • odoo google插件(2)
  • 自组装mid360便捷化bag包采集设备
  • Elasticsearch:Painless scripting 语言(一)
  • FFmpeg教程-三-播放pcm文件-1
  • 面向对象,常用类,集合,异常,JDBC,mysql数据库内容的复习,
  • Windows系统下查看电脑被远程访问历史记录
  • C语言快速学习笔记
  • 2024 MWC上海:创新力量驱动未来先行,移远智慧点亮数字蓝海
  • 上位机图像处理和嵌入式模块部署(mcu 项目1:固件编写)
  • py黑帽子学习笔记_burp
  • 【js】数组元素拼接、数组元素类型转换
  • Android调用onNewIntend的时刻
  • RPM包管理-rpm命令管理
  • 【linux/shell实战案例】在shell中插入Python代码及EOF解析
  • django开发-定时任务的使用
  • docker容器内的网络抓包
  • ES6 ...操作符
  • ES6系列(二)变量的解构赋值
  • Javascripit类型转换比较那点事儿,双等号(==)
  • JAVA之继承和多态
  • js
  • Linux下的乱码问题
  • maven工程打包jar以及java jar命令的classpath使用
  • Spring Cloud(3) - 服务治理: Spring Cloud Eureka
  • vue2.0项目引入element-ui
  • windows下如何用phpstorm同步测试服务器
  • 从输入URL到页面加载发生了什么
  • 翻译 | 老司机带你秒懂内存管理 - 第一部(共三部)
  • 高程读书笔记 第六章 面向对象程序设计
  • 关于 Cirru Editor 存储格式
  • 回顾 Swift 多平台移植进度 #2
  • 简单实现一个textarea自适应高度
  • 聊聊flink的BlobWriter
  • 浅谈web中前端模板引擎的使用
  • 问:在指定的JSON数据中(最外层是数组)根据指定条件拿到匹配到的结果
  • 用Node EJS写一个爬虫脚本每天定时给心爱的她发一封暖心邮件
  • 栈实现走出迷宫(C++)
  • 《TCP IP 详解卷1:协议》阅读笔记 - 第六章
  • HanLP分词命名实体提取详解
  • $emit传递多个参数_PPC和MIPS指令集下二进制代码中函数参数个数的识别方法
  • (2/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序
  • (23)Linux的软硬连接
  • (C#)Windows Shell 外壳编程系列9 - QueryInfo 扩展提示
  • (C语言)输入一个序列,判断是否为奇偶交叉数
  • (Java)【深基9.例1】选举学生会
  • (安全基本功)磁盘MBR,分区表,活动分区,引导扇区。。。详解与区别
  • (第二周)效能测试
  • (附源码)springboot 智能停车场系统 毕业设计065415
  • (离散数学)逻辑连接词
  • (亲测成功)在centos7.5上安装kvm,通过VNC远程连接并创建多台ubuntu虚拟机(ubuntu server版本)...
  • (一)WLAN定义和基本架构转
  • (转)linux自定义开机启动服务和chkconfig使用方法
  • (转)VC++中ondraw在什么时候调用的
  • **Java有哪些悲观锁的实现_乐观锁、悲观锁、Redis分布式锁和Zookeeper分布式锁的实现以及流程原理...