1、实验简介

参考网址:https://gitee.com/Lockzhiner-Electronics/lz3863/tree/master/apps/c2_wifi_tcp_client

1.1、实验目的

本实验旨在帮助学习者掌握 OpenHarmony 轻量系统中 WiFi + TCP 网络通信 的基本使用方法。通过本实验,你将学会:

  1. 理解 TCP 协议 的面向连接、可靠传输特性及客户端/服务端通信模型;
  2. 在开发板上以 STA 模式连接 WiFi 热点,通过 DHCP 获取 IP 地址;
  3. 使用 lwIP Socket API 创建 TCP 套接字,完成连接、发送、接收与关闭;
  4. 掌握 WiFi 连接与 TCP 通信的完整联调流程;
  5. 完成案例代码的编译、烧录与串口现象观察。

1.2、实验内容

本案例在 LZ3863-星闪开发板上实现 TCP 客户端 功能:先连接指定 WiFi 热点,再与 TCP 服务器建立连接,发送测试数据并持续接收服务器响应,全过程通过串口打印日志。

项目 说明
源文件 wifi_tcp_client_example.c(主程序)、wifi_connecter.c(WiFi 封装)
WiFi 模式 STA(站点/客户端)
目标 SSID lzdz
目标密码 88888888
TCP 服务器 IP 192.168.137.1
TCP 服务器端口 777
发送测试数据 wifi_tcp_test_date
任务线程 tcp_demo_task(栈大小 8192 字节)
初始化入口 APP_FEATURE_INIT(tcp_demo_entry)

典型联调拓扑:

┌─────────────────┐         WiFi          ┌─────────────────┐
│  PC / 手机热点   │ ◄──────────────────► │  LZ3863 开发板   │
│  SSID: lzdz     │                       │  (TCP Client)   │
│  IP: 192.168.   │      TCP :777         │                 │
│      137.1      │ ◄──────────────────► │                 │
│  (TCP Server)   │                       │                 │
└─────────────────┘                       └─────────────────┘

说明:默认服务器 IP 192.168.137.1 为 Windows 移动热点网关地址。若使用其他路由器或开发板作为 TCP 服务器,需同步修改 TCP_SERVER_IP 宏定义。

1.3、实验环境

项目 说明
硬件 LZ3863-星闪开发板、USB 数据线
软件 OpenHarmony v5.1.0 源码、hb 编译工具
网络环境 可连接的 WiFi 热点(SSID/密码与代码一致)
TCP 服务器 PC 端网络调试工具 / nc / Python 脚本,或 c3_wifi_tcp_server 案例
调试工具 串口助手(波特率 115200,8N1)
案例路径 applications/sample/wifi-iot/app/c2_wifi_tcp_client/

2、基础知识

2.1、TCP 协议概述

TCP(Transmission Control Protocol,传输控制协议) 是一种面向连接的、可靠的传输层协议,具有以下特点:

特性 说明
面向连接 通信前需通过三次握手建立连接,结束后四次挥手断开
可靠传输 通过序号、确认、重传机制保证数据不丢失、不重复
全双工 连接建立后,双方可同时发送和接收数据
字节流 数据以连续字节流形式传输,无固定报文边界

TCP 通信采用 客户端/服务端(C/S) 模型:

角色 职责 本实验对应
TCP 服务端 绑定端口、监听连接、接受客户端请求 PC 或另一块开发板
TCP 客户端 主动发起连接、发送/接收数据 本案例开发板

2.2、Socket 编程基础

Socket(套接字)是网络编程的抽象接口,lwIP 提供了与 BSD Socket 兼容的 API。TCP 客户端的典型流程:

socket()  →  创建套接字
    ↓
配置 sockaddr_in(IP + 端口)
    ↓
connect()  →  连接服务器(三次握手)
    ↓
send() / recv()  →  收发数据
    ↓
closesocket()  →  关闭连接

关键数据结构 sockaddr_in

struct sockaddr_in server_addr = {0};
server_addr.sin_family = AF_INET;              // IPv4
server_addr.sin_port = htons(port);            // 端口号(转网络字节序)
inet_pton(AF_INET, host, &server_addr.sin_addr); // IP 地址(字符串 → 二进制)

字节序转换:

函数 作用
htons() 主机字节序 → 网络字节序(16 位,用于端口)
htonl() 主机字节序 → 网络字节序(32 位,用于 IP)
inet_pton() 字符串 IP → 二进制网络地址
inet_ntoa() 二进制网络地址 → 字符串 IP

2.3、WiFi STA 连接与网络层

本案例在 TCP 通信之前,需先通过 ConnectToHotspot() 完成 WiFi 连接与 IP 获取。完整流程为:

启用 STA 模式 → 扫描热点 → 匹配 SSID → 关联连接
    → 启动 DHCP 客户端 → 获取 IP 地址 → 网络层就绪

获取 IP 后,开发板与 TCP 服务器处于同一局域网,方可进行 Socket 通信。

2.4、软件调用层次

本案例的软件调用层次如下:

应用层(wifi_tcp_client_example.c)
    ├── tcp_demo_entry()          ← APP_FEATURE_INIT 注册入口
    ├── tcp_demo_task()           ← 任务线程:WiFi 连接 + TCP 客户端
    └── tcp_client_test()         ← TCP 核心逻辑
            │
WiFi 封装层(wifi_connecter.c)
    └── ConnectToHotspot()        ← 扫描、连接、DHCP 获取 IP
            │
协议栈 / 驱动层
    ├── lwIP Socket API(socket/connect/send/recv)
    ├── lwIP 网络协议栈(TCP/IP、DHCP)
    └── WiFi 驱动(HMAC/DMAC)

2.5、核心 API 介绍

2.5.1、头文件
#include "cmsis_os2.h"
#include "lwip/sockets.h"
#include "ohos_init.h"
#include "osal_debug.h"
#include "wifi_connecter.h"
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
2.5.2、Socket API
API 名称 功能说明
socket(AF_INET, SOCK_STREAM, 0) 创建 IPv4 TCP 套接字,返回套接字描述符
inet_pton(af, src, dst) 将字符串 IP 地址转换为网络字节序二进制形式
connect(sockfd, addr, addrlen) 向 TCP 服务器发起连接(三次握手)
send(sockfd, buf, len, flags) 向已连接套接字发送数据,返回实际发送字节数
recv(sockfd, buf, len, flags) 从已连接套接字接收数据,返回实际接收字节数
closesocket(sockfd) 关闭套接字,释放资源
2.5.3、WiFi 与应用层 API
API 名称 功能说明
ConnectToHotspot(ssid, password) 扫描并连接指定热点,完成 DHCP 获取 IP
DisconnectWithHotspot() 停止 DHCP 并断开 WiFi 连接
osThreadNew(func, arg, &attr) 创建 RTOS 线程
osDelay(ticks) 线程延时,100 ticks ≈ 1 秒(tick = 10 ms)
APP_FEATURE_INIT(func) 注册应用特性初始化入口,系统启动后自动执行

3、程序设计

3.1、程序架构

本案例目录结构

c2_wifi_tcp_client/
├── wifi_tcp_client_example.c   # TCP 客户端主程序
├── wifi_connecter.c            # WiFi 连接封装实现
├── wifi_connecter.h            # 封装接口头文件
├── BUILD.gn                    # GN 编译配置
├── README_zh.md                # 案例简要说明
└── 实验手册.md                  # 本实验手册

程序执行流程:

系统启动
    │
    ▼
tcp_demo_entry()                ← APP_FEATURE_INIT 注册,自动执行
    │
    ▼
osThreadNew(tcp_demo_task)      ← 创建 TCP 示例任务线程
    │
    ▼
tcp_demo_task()
    ├── ConnectToHotspot()      ← 连接 WiFi 热点,DHCP 获取 IP
    ├── osDelay(800)            ← 等待网络稳定(约 8 秒)
    └── tcp_client_test()       ← 启动 TCP 客户端通信
            ├── socket()        ← 创建套接字
            ├── connect()       ← 连接 TCP 服务器
            ├── send()          ← 发送测试数据
            ├── recv() 循环     ← 持续接收服务器响应
            └── closesocket()   ← 关闭连接

3.2、源文件说明

文件 说明
wifi_tcp_client_example.c TCP 客户端主程序,包含 WiFi 连接、TCP 通信及任务线程
wifi_connecter.c WiFi 封装,实现 ConnectToHotspotStartHotspotDisconnectWithHotspot
wifi_connecter.h 封装接口声明
BUILD.gn 编译配置,生成 wifi_tcp_client_example 静态库

3.3、关键代码分析

(1)WiFi 与 TCP 配置参数
#define WIFI_SSID "lzdz"
#define WIFI_PASSWORD "88888888"

#define TCP_SERVER_IP "192.168.137.1"
#define TCP_SERVER_PORT 777

static char request_data[50] = "wifi_tcp_test_date";
static char response_data[100];

实验前请根据实际网络环境修改上述宏定义,确保 SSID、密码、服务器 IP 与端口正确。

(2)系统入口 — tcp_demo_entry

通过 APP_FEATURE_INIT 注册应用入口,创建 tcp_demo_task 线程:

static void tcp_demo_entry(void)
{
    osThreadAttr_t attr = {
        .name = "tcp_demo_task",
        .stack_size = 8192,
        .priority = osPriorityNormal
    };

    if (osThreadNew(tcp_demo_task, NULL, &attr) == NULL)
    {
        printf("[tcp_demo_entry] Failed to create tcp_demo_task!\r\n");
    }
}

APP_FEATURE_INIT(tcp_demo_entry);
(3)任务线程 — tcp_demo_task

任务线程先完成 WiFi 连接,延时等待网络稳定后再启动 TCP 客户端:

static void tcp_demo_task(void *arg)
{
    (void)arg;

    printf("Starting Wi-Fi connection to SSID: %s...\r\n", WIFI_SSID);
    if (ConnectToHotspot(WIFI_SSID, WIFI_PASSWORD) != 0)
    {
        printf("Failed to connect to AP.\r\n");
        return;
    }

    printf("Wi-Fi connected successfully.\r\n");

    osDelay(800);   // 等待约 8 秒,确保网络栈稳定

    tcp_client_test(TCP_SERVER_IP, TCP_SERVER_PORT);
}
(4)TCP 客户端核心 — tcp_client_test

tcp_client_test() 实现完整的 TCP 客户端通信流程:

void tcp_client_test(const char *host, unsigned short port)
{
    ssize_t ret = 0;

    // 1. 创建 TCP 套接字
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        printf("Failed to create socket! errno=%d\r\n", errno);
        return;
    }

    // 2. 配置服务器地址
    struct sockaddr_in server_addr = {0};
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);
    if (inet_pton(AF_INET, host, &server_addr.sin_addr) <= 0)
    {
        printf("inet_pton failed! Invalid IP address.\r\n");
        closesocket(sockfd);
        return;
    }

    // 3. 连接 TCP 服务器
    printf("Attempting to connect to %s:%d...\r\n", host, port);
    if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
    {
        printf("connect tcp server failed! errno=%d\r\n", errno);
        closesocket(sockfd);
        return;
    }
    printf("Connected to TCP server %s successfully!\r\n", host);

    // 4. 发送测试数据
    ret = send(sockfd, request_data, strlen(request_data), 0);
    if (ret < 0)
    {
        printf("send request failed! errno=%d\r\n", errno);
    }
    else
    {
        printf("Sent request{%s} %ld bytes to TCP server!\r\n", request_data, ret);
    }

    // 5. 持续接收服务器响应
    while (1)
    {
        memset(response_data, 0, sizeof(response_data));
        ret = recv(sockfd, response_data, sizeof(response_data) - 1, 0);
        if (ret <= 0)
        {
            printf("recv_data failed or connection closed, ret=%ld, errno=%d\r\n", ret, errno);
            break;
        }
        response_data[ret] = '\0';
        printf("Received data{%s} from server!\r\n", response_data);
        osDelay(100);   // 防止忙等待
    }

    // 6. 关闭套接字
    closesocket(sockfd);
}
(5)WiFi 封装层 — ConnectToHotspot 概要

ConnectToHotspot()wifi_connecter.c 中实现,主要步骤:

  1. 调用 wifi_sta_enable() 启用 STA 模式;
  2. 循环执行 wifi_sta_scan() 扫描,get_match_network() 匹配目标 SSID;
  3. 调用 wifi_sta_connect() 发起关联,等待 WIFI_CONNECTED 状态;
  4. wlan0 接口上启动 DHCP 客户端,获取 IP 地址并打印。

3.4、程序执行流程

TCP 服务器 (PC/开发板) WiFi 热点 开发板 (TCP Client) TCP 服务器 (PC/开发板) WiFi 热点 开发板 (TCP Client) 连接关闭或出错时 closesocket() APP_FEATURE_INIT → tcp_demo_entry osThreadNew(tcp_demo_task) ConnectToHotspot("lzdz") 关联成功 + DHCP 分配 IP osDelay(800) 等待网络稳定 socket() + connect(192.168.137.1:777) 三次握手完成 send("wifi_tcp_test_date") 服务器响应数据 recv() 循环打印接收内容

4、编译步骤

以下步骤只需在首次编译时完成 4.1~4.3 的配置注册。

4.1、确认案例目录

确认案例已位于 OpenHarmony 源码目录下:

applications/sample/wifi-iot/app/c2_wifi_tcp_client/
├── wifi_tcp_client_example.c
├── wifi_connecter.c
├── wifi_connecter.h
├── BUILD.gn
└── 实验手册.md

若从外部复制,请将 c2_wifi_tcp_client 目录放到上述 app/ 路径下。

4.2、修改 BUILD.gn(注册编译组件)

编辑 applications/sample/wifi-iot/app/BUILD.gn,在 features 列表中添加本案例:

lite_component("app") {
  features = [
    "startup",
    "c2_wifi_tcp_client:wifi_tcp_client_example",   // 添加此行
  ]
}

4.3、修改 SDK 配置文件

步骤 1:编辑 device/soc/hisilicon/ws63v100/sdk/build/config/target_config/ws63/config.py

找到 'ws63-liteos-app' 配置段,在其 'ram_component' 列表中添加:

"wifi_tcp_client_example"

步骤 2:编辑 device/soc/hisilicon/ws63v100/sdk/libs_url/ws63/cmake/ohos.cmake

找到 "ws63-liteos-app" 对应的 set(COMPONENT_LIST 部分,添加:

"wifi_tcp_client_example"

4.4、编译固件

在 OpenHarmony 源码根目录下执行编译:

rm -rf out
hb set -root .
# 通过上下方向键选择 ws63 对应的编译分支(如 nearlink_dk_3863 / ws63-liteos-app)
hb build -f

编译成功后,使用开发板配套烧录工具将固件烧写到 LZ3863-星闪开发板。

4.5、修改网络参数(可选)

烧录前,若实际 WiFi 热点或 TCP 服务器地址与默认值不同,请编辑 wifi_tcp_client_example.c 中的宏定义:

#define WIFI_SSID "lzdz"              // 改为实际热点名称
#define WIFI_PASSWORD "88888888"      // 改为实际热点密码
#define TCP_SERVER_IP "192.168.137.1" // 改为 TCP 服务器 IP
#define TCP_SERVER_PORT 777         // 改为 TCP 服务器端口

修改后需重新编译并烧录。


5、运行结果

5.1、硬件与网络准备

方式一:PC 移动热点 + PC 端 TCP 服务器(推荐)

  1. 在 PC 上开启移动热点,SSID 设为 lzdz,密码设为 88888888

  2. 确认 PC 热点网关 IP 为 192.168.137.1(Windows 默认),若不是请修改 TCP_SERVER_IP

  3. 在 PC 上启动 TCP 服务器,监听端口 777

    # 方式 A:使用 netcat(Linux / macOS)
    nc -l 777
    
    # 方式 B:使用 Python
    python3 -c "
    import socket
    s = socket.socket()
    s.bind(('0.0.0.0', 777))
    s.listen(1)
    print('Waiting for connection on port 777...')
    conn, addr = s.accept()
    print(f'Connected from {addr}')
    conn.send(b'Hello from TCP server!')
    while True:
        data = conn.recv(1024)
        if not data: break
        print(f'Received: {data.decode()}')
    conn.close()
    "
    
  4. 开发板上电或复位,通过 USB 连接 PC 打开串口助手(115200,8N1)。

方式二:使用 c3_wifi_tcp_server 案例联调

  1. 准备可连接的 WiFi 路由器或手机热点(SSID/密码与代码一致);
  2. 另一块开发板烧录 c3_wifi_tcp_server 固件作为 TCP 服务端;
  3. 查看服务端串口日志中打印的 IP 地址,将其填入本案例的 TCP_SERVER_IP 宏定义后重新编译烧录客户端固件。

建议:先启动 TCP 服务器并确认监听成功,再复位开发板,以提高首次连接成功率。

5.2、串口配置

参数
波特率 115200
数据位 8
停止位 1
校验位
流控

5.3、预期串口输出

烧录固件并复位后,串口助手可观察到完整的 WiFi 连接与 TCP 通信过程:

Starting Wi-Fi connection to SSID: lzdz...
Start Scan !
[WIFI_STA_SAMPLE] Scan done!.
STA try connect.
[WIFI_STA_SAMPLE] Connect succ!.
STA DHCP start.
STA DHCP bound success.
STA IP 192.168.137.x
Connect success.
Wi-Fi connected successfully.
Attempting to connect to 192.168.137.1:777...
Connected to TCP server 192.168.137.1 successfully!
Sent request{wifi_tcp_test_date} 18 bytes to TCP server!
Received data{Hello from TCP server!} from server!

其中:

  • Start Scan ! / Scan done! 表示 WiFi 热点扫描完成;
  • Connect succ! / STA IP 192.168.137.x 表示 WiFi 关联成功并获取 IP;
  • Wi-Fi connected successfully. 表示应用层确认 WiFi 就绪;
  • Connected to TCP server ... successfully! 表示 TCP 三次握手完成;
  • Sent request{wifi_tcp_test_date} 表示测试数据发送成功;
  • Received data{...} from server! 表示收到服务器响应。

5.4、PC 端 TCP 服务器预期现象

若使用 netcat 或 Python 脚本作为服务器,PC 端可观察到:

Waiting for connection on port 777...
Connected from ('192.168.137.x', xxxxx)
Received: wifi_tcp_test_date

表示开发板已成功连接并发送测试数据。

5.5、结果分析

现象 说明
输出 Wi-Fi connected successfully. WiFi 连接与 DHCP 获取 IP 成功
输出 Connected to TCP server ... successfully! TCP 连接建立成功
输出 Sent request{wifi_tcp_test_date} 数据发送成功
输出 Received data{...} from server! 服务器响应接收成功
输出 Failed to connect to AP. WiFi 连接失败,检查 SSID/密码
输出 connect tcp server failed! TCP 连接失败,检查服务器 IP/端口及防火墙
输出 recv_data failed or connection closed 服务器主动关闭连接或网络中断

5.6、常见问题排查

问题 可能原因 解决方法
反复 Can not find AP 热点未开启或 SSID/密码不匹配 确认热点已开启,宏定义与实际一致
WiFi 连接成功但 TCP 连接失败 服务器未启动或 IP/端口错误 先启动 TCP 服务器,核对 TCP_SERVER_IP 和端口
connect tcp server failed! errno=113 路由不可达,IP 地址错误 确认开发板与服务器在同一网段
connect tcp server failed! errno=111 服务器端口未监听 检查 PC 防火墙,确认端口 777 已开放
PC 端收不到数据 防火墙拦截 关闭防火墙或添加端口 777 入站规则
编译报错找不到组件 BUILD.gn 或 config.py 未修改 逐步核对 4.2、4.3 节的配置项
仅收到一次数据后停止 服务器关闭连接 正常现象,recv() 返回 0 时客户端退出循环

6、实验扩展

完成基本实验后,可尝试以下扩展练习:

  1. 修改通信内容:更改 request_data 字符串,观察 PC 端接收到的数据变化;
  2. 双向通信:在 recv() 循环中增加 send() 回发逻辑,实现简单的请求-响应交互;
  3. 配合 TCP 服务端案例:使用 c3_wifi_tcp_server 案例,实现两块开发板之间的 TCP 通信;
  4. 连接超时处理:在 connect() 前设置套接字超时(setsockopt + SO_RCVTIMEO),避免无限阻塞;
  5. 断线重连:TCP 连接断开后自动重新 connect(),实现简单的断线重连机制;
  6. 改用 UDP 通信:参考后续 UDP 案例,对比 TCP 与 UDP 在可靠性、效率上的差异;
  7. HTTP 应用:在 TCP 基础上发送 HTTP GET 请求,理解应用层协议与传输层的关系。
Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐