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
#endif

int 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 AP
        osDelay(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 1
        ret = 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 AP
    printf("Disconnect: %d\r\n", errCode);

    errCode = UnRegisterWifiEvent(&g_defaultWifiEventListener);
    printf("UnRegisterWifiEvent: %d\r\n", errCode);

    RemoveDevice(netId); // remove AP config
    printf("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 socket
    int 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.0

    retval = 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 debug

do_cleanup:
    printf("do_cleanup...\r\n");

    close(sockfd);

    DisconnectWithHotspot(netId);
}

SYS_RUN(TcpServerTest);

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

相关推荐
坚果派·白晓明5 小时前
常用URL语法传输数据开源命令行工具curl鸿蒙化构建过程深度解析
开源·openharmony·开源鸿蒙·开源软件termony
坚果派·白晓明7 小时前
通过开源鸿蒙终端工具Termony完成Zlib 命令行工具构建过程深度解读
openharmony·开源鸿蒙·开源软件termony
坚果派·白晓明16 小时前
Tree 命令行工具鸿蒙化构建过程问题及解决方法
openharmony·开源鸿蒙·开源软件termony
坚果派·白晓明1 天前
开源鸿蒙化构建GNU Tar 1.35:完整过程与验证
openharmony·开源鸿蒙·开源软件termony
坚果派·白晓明1 天前
通过开源鸿蒙终端工具Termony完成Libarchive 命令行工具构建过程深度解读
openharmony·开源鸿蒙·开源软件termony
坚果派·白晓明1 天前
通过开源鸿蒙终端工具Termony完成Zstd 命令行工具构建过程深度解读
openharmony·开源鸿蒙·开源软件termony
坚果派·白晓明2 天前
通过开源鸿蒙终端工具Termony完成Vim命令行工具构建过程深度解读
vim·openharmony·开源鸿蒙·开源软件termony
坚果派·白晓明2 天前
通过开源鸿蒙终端工具Termony完成Coremark 命令行工具构建过程深度解读
openharmony·命令行工具·开源鸿蒙·开源软件termony
坚果派·白晓明2 天前
通过开源鸿蒙终端工具Termony完成Ncurses 命令行工具构建过程深度解读
openharmony·开源鸿蒙·开源软件termony
A懿轩A3 天前
【2025版 OpenHarmony】GitCode 口袋工具 v1.0.1 更新发布:Flutter + HarmonyOS 封装导航栏进行跳转
flutter·harmonyos·openharmony·gitcode·开源鸿蒙