本文为 WIZnet W55RP20 芯片 C 语言教程第 3 篇,基于官方示例工程编写,代码均经过实际验证,可直接参考使用。
版权声明:本文为 WIZnet 官方原创技术文章,转载请注明出处。


前言

在上一篇教程中,我们已经完成了 DHCP 自动获取 IP 地址测试。开发板可以通过路由器自动获取 IP、子网掩码、网关和 DNS,并通过串口输出确认 DHCP 获取结果。

完成基础联网方式验证后,接下来就可以进入更实际的网络通信测试。很多设备在连接云平台、HTTPS 服务、安全网关或远程服务器时,不仅需要完成 TCP 连接,还需要保证数据传输的安全性,因此通常会使用 TLS/SSL 对通信数据进行加密。

本文将基于 W55RP20 芯片,使用 pico-vscode 作为开发环境,通过 C 语言 运行 wizchip_tcp_client_over_ssl 示例。开发板会作为 TCP Client,主动连接电脑端 OpenSSL Server,完成 TLS 握手后发送测试数据。

学完本文,你将掌握:

  • SSL/TLS 的基本作用和优势
  • wizchip_tcp_client_over_ssl 示例的运行流程
  • 如何在电脑端启动 OpenSSL Server
  • 如何配置开发板 IP 和目标 SSL Server 地址
  • 如何通过 mbedTLS 完成 TLS 握手
  • 如何查看串口日志并判断 SSL 通信是否成功

系列教程学习路径

本系列将围绕 W55RP20 芯片 C 语言开发进行讲解,循序渐进完成从基础联网到网络通信应用的学习:

  1. 第 1 篇:Network 静态 IP 联网测试
  2. 第 2 篇:DHCP 自动获取 IP 地址测试
  3. 第 3 篇:TCP Client over SSL 加密通信测试(本文)
  4. 第 4 篇:TCP Server 多 Socket 通信测
  5. 第 5 篇:UDP 单播数据通信
  6. 第 6 篇:DNS 域名解析
  7. 第 7 篇:HTTP Client 客户端请求
  8. 第 8 篇:HTTP Server 服务端搭建
  9. 第 9 篇:MQTT 协议基础通信
  10. 第 10 篇:Modbus TCP 工业通信

建议先完成 Network 静态 IP 联网测试和 DHCP 自动获取 IP 地址测试,确认开发板网络参数获取、网线连接和 ping 连通性均正常后,再继续进行本文的 SSL 通信实验。


目录


1 TCP Client over SSL 示例简介

1.1 SSL 是什么

SSL(Secure Sockets Layer)是一种用于网络通信加密的安全协议,用于在客户端和服务器之间建立安全通信通道。现在实际使用中更多是 TLS(Transport Layer Security),TLS 可以看作是 SSL 的升级版本,但很多资料和工具中仍习惯称为 SSL。

普通 TCP 建立连接后会直接传输明文数据,而 TCP over SSL 会在 TCP 连接建立后进行 TLS/SSL 握手,协商加密算法和密钥。握手完成后,后续数据会经过加密处理。

通信方式 说明
普通 TCP 建立连接后直接收发明文数据
TCP over SSL 先建立 TCP 连接,再完成 TLS/SSL 握手,之后收发加密数据

1.2 SSL 的优点

相比普通 TCP 明文通信,SSL/TLS 主要有以下优点:

优点 说明
数据加密 传输内容经过加密处理,降低数据被直接读取的风险
身份认证 可通过证书验证服务器身份
数据完整性 可以检测数据传输过程中是否被篡改
适合云端通信 许多云平台、HTTPS 服务和安全网关都要求使用 TLS/SSL
安全性更高 适合传输设备状态、控制指令、传感器数据等重要内容

1.3 本示例的作用

本示例会让 W55RP20 开发板作为 TCP Client,连接电脑端 OpenSSL Server。

本文示例中,开发板静态 IP 为 192.168.1.188,目标 SSL Server 地址为 192.168.1.120:5000,本地端口为 50000。连接成功后,程序会执行 TLS 握手,并向 SSL Server 发送测试数据 W5x00 TCP over SSL test

该示例主要验证以下内容:

测试项目 说明
TCP Client 连接 开发板主动连接电脑端 OpenSSL Server
TLS 握手 使用 mbedTLS 完成 SSL/TLS 握手
加密套件协商 输出当前协商得到的 Ciphersuite
加密数据发送 开发板通过 TLS 连接发送测试数据
串口日志 通过 VS Code 串口终端查看连接、握手和发送结果

1.4 TCP Client over SSL 工作流程

TCP Client over SSL 的整体工作流程如下:

系统启动
↓
初始化定时器、SPI 和 WIZnet 芯片
↓
配置静态网络参数
↓
创建 TCP Client Socket
↓
连接电脑端 OpenSSL Server
↓
执行 TLS 握手
↓
输出协商得到的 Ciphersuite
↓
通过 TLS 发送测试数据
↓
串口打印发送结果

从流程可以看到,本示例的核心是:开发板先完成 TCP 连接,再通过 mbedTLS 完成 TLS 握手,最后发送加密数据。


2 准备工作

2.1 软件准备

本次实验使用 pico-vscode 作为主要开发环境。W55RP20 C 语言例程依赖 Pico C/C++ 开发环境,需要先安装对应环境,再通过桌面生成的 pico-vscode 快捷方式打开工程。

需要准备的软件如下:

软件名称 版本要求 下载地址 说明
Pico C/C++ 开发环境 最新稳定版 Pico SDK 官方仓库 提供 Pico SDK、CMake、编译工具链等基础环境
pico-vscode 随开发环境生成 安装 Pico C/C++ 开发环境后生成 用于打开和编译 Pico / W55RP20 C 语言工程
W55RP20 官方示例工程 最新稳定版 WIZnet 官方示例工程 包含芯片初始化、SPI 驱动和 TCP over SSL 示例代码
OpenSSL / SSL Server 工具 按需安装 按系统环境选择 用于在电脑端搭建 SSL Server,接收开发板发送的数据

注意:建议使用桌面上的 pico-vscode 打开工程,不要直接使用普通 VS Code 打开,否则可能会出现工具链、环境变量或 CMake 配置无法识别的问题。


2.2 硬件准备

如图所示,W55RP20-EVB-Pico 开发板实物图。

在这里插入图片描述

需要准备以下硬件:

  • W55RP20 开发板 × 1
  • USB 数据线 × 1
  • 标准网线 × 1
  • 路由器或交换机 × 1

提示:电脑和开发板需要连接到同一个局域网中,否则开发板无法连接电脑端 OpenSSL Server。


3 pico-vscode 工程打开与 UF2 烧录

运行 TCP Client over SSL 示例前,需要先通过 pico-vscode 打开 W55RP20 C 语言示例工程,并编译生成 .uf2 文件。

整体流程如下:

  1. 安装 Pico C/C++ 开发环境
  2. 安装完成后,桌面会出现 pico-vscode 快捷方式
  3. 双击打开 pico-vscode
  4. pico-vscode 中打开 W55RP20 C 语言示例工程
  5. 选择需要运行的 wizchip_tcp_client_over_ssl 示例
  6. 编译工程,生成对应的 .uf2 文件
  7. 按住开发板上的 BOOTSEL 按键不放
  8. 使用 USB 数据线连接开发板与电脑
  9. 电脑识别出名为 RPI-RP2 的 U 盘后,松开 BOOTSEL 按键
  10. 将编译生成的 .uf2 文件拖入 RPI-RP2 U 盘
  11. 开发板自动重启,程序烧录完成

烧录完成后,开发板会运行刚刚编译生成的 TCP Client over SSL 示例程序,此时可以打开串口终端查看运行日志。


4 硬件连接与开发环境配置

4.1 硬件连接

硬件连接主要分为两步:

  1. 使用 USB 数据线连接开发板与电脑,用于供电、下载程序和串口输出
  2. 使用网线连接开发板以太网接口与路由器或交换机 LAN 口

如图所示为硬件连接示意图。

在这里插入图片描述


4.2 串口终端配置

程序烧录完成后,打开 VS Code 串口终端或其他串口调试工具查看运行日志即可。串口号以电脑实际识别到的端口为准,本文实验中为 COM104


5 电脑端 OpenSSL Server 配置

运行本示例前,需要先在电脑端启动 OpenSSL Server,等待开发板主动连接。

本文使用 OpenSSL 在电脑端开启 SSL Server,监听端口为 5000。在命令提示符中进入证书文件所在目录,然后执行以下命令:

cd /d D:\ssl_test
openssl s_server -accept 5000 -cert cert.pem -key key.pem -tls1_2 -state -debug

命令参数说明如下:

参数 说明
cd /d D:\ssl_test 切换到证书和密钥文件所在目录
openssl s_server 启动 OpenSSL SSL Server
-accept 5000 监听本机 5000 端口
-cert cert.pem 指定服务器证书文件
-key key.pem 指定服务器私钥文件
-tls1_2 使用 TLS 1.2 协议
-state 打印 SSL/TLS 状态变化
-debug 打印调试信息,便于观察握手和数据收发过程

本文示例中的目标 SSL Server 参数如下:

参数 设置
Server IP 192.168.1.120
Server Port 5000
协议类型 SSL/TLS Server
证书文件 cert.pem
私钥文件 key.pem

开发板代码中对应的目标服务器配置为:

static uint8_t g_ssl_server_ip[4] = {192, 168, 1, 120};
#define PORT_SSL_SERVER 5000

注意:g_ssl_server_ip 需要填写电脑当前局域网 IP,PORT_SSL_SERVER 需要与 OpenSSL Server 监听端口一致。如果电脑 IP 发生变化,需要同步修改代码中的目标服务器 IP。


6 TCP Client over SSL 示例代码

下面给出 TCP Client over SSL 示例完整代码。代码中已经对关键部分添加中文注释,实际使用时需要根据当前局域网环境修改开发板静态 IP、目标 SSL Server IP 和监听端口。

6.1 完整代码

/**
 * Copyright (c) 2021 WIZnet Co.,Ltd
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "pico/stdlib.h"

#include "port_common.h"
#include "socket.h"
#include "timer.h"
#include "wizchip_conf.h"
#include "wizchip_spi.h"

#include "mbedtls/error.h"
#include "mbedtls/ssl.h"

/* 收发缓冲区大小 */
#define ETHERNET_BUF_MAX_SIZE (1024 * 2)

/* 使用 Socket0 作为 SSL Client */
#define SOCKET_SSL_CLIENT 0

/* 电脑端 OpenSSL Server 监听端口 */
#define PORT_SSL_SERVER 5000

/* 开发板本地端口 */
#define LOCAL_PORT_SSL_CLIENT 50000

/* 开发板静态网络参数 */
static wiz_NetInfo g_net_info = {
    .mac = {0x00, 0x08, 0xDC, 0x12, 0x34, 0x56}, // MAC 地址
    .ip = {192, 168, 1, 188},                     // 开发板静态 IP 地址
    .sn = {255, 255, 255, 0},                     // 子网掩码
    .gw = {192, 168, 1, 1},                       // 网关地址
    .dns = {8, 8, 8, 8},                          // DNS 服务器地址
#if _WIZCHIP_ > W5500
    .lla = {
        0xfe, 0x80, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00,
        0x02, 0x08, 0xdc, 0xff,
        0xfe, 0x57, 0x57, 0x25
    },
    .gua = {
        0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00
    },
    .sn6 = {
        0xff, 0xff, 0xff, 0xff,
        0xff, 0xff, 0xff, 0xff,
        0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00
    },
    .gw6 = {
        0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00
    },
    .dns6 = {
        0x20, 0x01, 0x48, 0x60,
        0x48, 0x60, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x88, 0x88
    },
    .ipmode = NETINFO_STATIC_ALL
#else
    .dhcp = NETINFO_STATIC
#endif
};

/* 电脑端 OpenSSL Server IP 地址 */
static uint8_t g_ssl_server_ip[4] = {192, 168, 1, 120};
/* TLS 收发缓冲区 */
static uint8_t g_ssl_buf[ETHERNET_BUF_MAX_SIZE];
/* 毫秒计数,用于 mbedTLS 超时处理 */
static volatile uint32_t g_msec_cnt = 0;

/* mbedTLS SSL 上下文和配置 */
static mbedtls_ssl_context g_ssl;
static mbedtls_ssl_config g_conf;

/* 支持的 TLS 加密套件列表 */
static const int g_ciphersuites[] = {
    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
    MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA256,
    MBEDTLS_TLS_RSA_WITH_AES_128_GCM_SHA256,
    MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA256,
    0
};

/* 1ms 定时器回调 */
static void repeating_timer_callback(void) {
    g_msec_cnt++;
}

static uint32_t millis(void) {
    return g_msec_cnt;
}

/* 打印 mbedTLS 错误信息 */
static void print_mbedtls_error(const char *name, int err) {
    char err_buf[128];

    mbedtls_strerror(err, err_buf, sizeof(err_buf));
    printf("%s failed: -0x%04X (%s)\r\n", name, -err, err_buf);
}

/* mbedTLS 随机数回调 */
static int ssl_random_callback(void *ctx, unsigned char *output, size_t len) {
    (void)ctx;

    for (size_t i = 0; i < len; i++) {
        output[i] = (unsigned char)(rand() & 0xFF);
    }

    return 0;
}

/* mbedTLS 发送回调,底层调用 WIZnet send() */
static int tcp_send(void *ctx, const unsigned char *buf, size_t len) {
    uint8_t sn = (uint8_t)(uintptr_t)ctx;
    int32_t ret = send(sn, (uint8_t *)buf, (uint16_t)len);

    if (ret == SOCK_BUSY) {
        return MBEDTLS_ERR_SSL_WANT_WRITE;
    }

    return ret;
}

/* mbedTLS 接收回调,底层调用 WIZnet recv() */
static int tcp_recv(void *ctx, unsigned char *buf, size_t len) {
    uint8_t sn = (uint8_t)(uintptr_t)ctx;
    int32_t ret = recv(sn, buf, (uint16_t)len);

    if (ret == SOCK_BUSY) {
        return MBEDTLS_ERR_SSL_WANT_READ;
    }

    return ret;
}

/* 带超时的接收回调,供 TLS 握手和读取数据使用 */
static int tcp_recv_timeout(void *ctx, unsigned char *buf, size_t len, uint32_t timeout) {
    uint8_t sn = (uint8_t)(uintptr_t)ctx;
    uint32_t start_ms = millis();
    uint16_t recv_len = 0;

    do {
        if (getSn_SR(sn) == SOCK_CLOSED) {
            return -1;
        }

        getsockopt(sn, SO_RECVBUF, &recv_len);

        if (recv_len > 0) {
            if (len > recv_len) {
                len = recv_len;
            }

            return tcp_recv(ctx, buf, len);
        }

        sleep_ms(1);
    } while ((millis() - start_ms) < timeout);

    return MBEDTLS_ERR_SSL_TIMEOUT;
}

/* 初始化 mbedTLS SSL Client */
static int ssl_client_init(uint8_t sn) {
    int ret;

    mbedtls_ssl_init(&g_ssl);
    mbedtls_ssl_config_init(&g_conf);

    ret = mbedtls_ssl_config_defaults(&g_conf,
                                      MBEDTLS_SSL_IS_CLIENT,
                                      MBEDTLS_SSL_TRANSPORT_STREAM,
                                      MBEDTLS_SSL_PRESET_DEFAULT);

    if (ret != 0) {
        print_mbedtls_error("mbedtls_ssl_config_defaults", ret);
        return ret;
    }

    /* 本地测试关闭证书校验,实际项目建议开启证书验证 */
    mbedtls_ssl_conf_authmode(&g_conf, MBEDTLS_SSL_VERIFY_NONE);
    /* 配置随机数、加密套件和读取超时时间 */
    mbedtls_ssl_conf_rng(&g_conf, ssl_random_callback, NULL);
    mbedtls_ssl_conf_ciphersuites(&g_conf, g_ciphersuites);
    mbedtls_ssl_conf_read_timeout(&g_conf, RECV_TIMEOUT);

    ret = mbedtls_ssl_setup(&g_ssl, &g_conf);

    if (ret != 0) {
        print_mbedtls_error("mbedtls_ssl_setup", ret);
        return ret;
    }

    /* 将 mbedTLS 与 WIZnet Socket 收发函数绑定 */
    mbedtls_ssl_set_bio(&g_ssl,
                        (void *)(uintptr_t)sn,
                        tcp_send,
                        tcp_recv,
                        tcp_recv_timeout);

    return 0;
}

/* 执行 TLS 握手 */
static int ssl_handshake(void) {
    int ret;

    printf("TLS handshake start\r\n");

    while ((ret = mbedtls_ssl_handshake(&g_ssl)) != 0) {
        if ((ret != MBEDTLS_ERR_SSL_WANT_READ) &&
            (ret != MBEDTLS_ERR_SSL_WANT_WRITE)) {
            print_mbedtls_error("mbedtls_ssl_handshake", ret);
            return ret;
        }
    }

    printf("TLS handshake OK\r\n");
    printf("Ciphersuite: %s\r\n", mbedtls_ssl_get_ciphersuite(&g_ssl));

    return 0;
}

/* 通过 TLS 连接发送完整数据 */
static int ssl_write_all(const uint8_t *buf, size_t len) {
    size_t written = 0;

    while (written < len) {
        int ret = mbedtls_ssl_write(&g_ssl, buf + written, len - written);

        if (ret > 0) {
            written += (size_t)ret;
            continue;
        }

        if ((ret == MBEDTLS_ERR_SSL_WANT_READ) ||
            (ret == MBEDTLS_ERR_SSL_WANT_WRITE)) {
            continue;
        }

        print_mbedtls_error("mbedtls_ssl_write", ret);
        return ret;
    }

    return (int)written;
}

int main() {
    int32_t ret;

    stdio_init_all();
    sleep_ms(3000);
    srand(time_us_32());

    printf("\r\n=== WIZnet TCP client over SSL ===\r\n");
    printf("Boot OK, initializing timer\r\n");

    /* 初始化 1ms 定时器,供 mbedTLS 超时处理使用 */
    wizchip_1ms_timer_initialize(repeating_timer_callback);

    printf("Initializing WIZchip SPI\r\n");
    /* 初始化 WIZnet SPI 接口 */
    wizchip_spi_initialize();

    printf("Initializing WIZchip critical section\r\n");
    /* 初始化临界区 */
    wizchip_cris_initialize();

    printf("Resetting WIZchip\r\n");
    wizchip_reset();

    printf("Initializing WIZchip, waiting for PHY link if needed\r\n");
    wizchip_initialize();

    printf("Checking WIZchip version\r\n");
    wizchip_check();

    printf("Applying network configuration\r\n");
    /* 写入网络参数并打印当前网络信息 */
    network_initialize(g_net_info);
    print_network_information(g_net_info);

    printf("TCP client over SSL example start\r\n");
    printf("Target SSL server: %d.%d.%d.%d:%d\r\n",
           g_ssl_server_ip[0],
           g_ssl_server_ip[1],
           g_ssl_server_ip[2],
           g_ssl_server_ip[3],
           PORT_SSL_SERVER);
    printf("Local socket port: %d\r\n", LOCAL_PORT_SSL_CLIENT);

    /* 初始化 SSL Client */
    ret = ssl_client_init(SOCKET_SSL_CLIENT);

    if (ret != 0) {
        while (1)
            ;
    }

    while (1) {
        /* 创建 TCP Client Socket */
        ret = socket(SOCKET_SSL_CLIENT, Sn_MR_TCP, LOCAL_PORT_SSL_CLIENT, SF_TCP_NODELAY);

        if (ret != SOCKET_SSL_CLIENT) {
            printf("Socket failed: %ld\r\n", ret);

            while (1)
                ;
        }

        printf("Socket opened\r\n");

        /* 主动连接电脑端 OpenSSL Server */
        ret = connect(SOCKET_SSL_CLIENT, g_ssl_server_ip, PORT_SSL_SERVER);
        printf("Socket status after connect: 0x%02X\r\n", getSn_SR(SOCKET_SSL_CLIENT));

        if (ret == SOCK_OK) {
            break;
        }

        printf("Connect failed: %ld, retrying in 1 second\r\n", ret);
        close(SOCKET_SSL_CLIENT);
        sleep_ms(1000);
    }

    printf("TCP connected\r\n");

    /* TCP 连接成功后执行 TLS 握手 */
    ret = ssl_handshake();

    if (ret != 0) {
        close(SOCKET_SSL_CLIENT);

        while (1)
            ;
    }

    /* TLS 握手成功后发送测试数据 */
    const char *msg = "W5x00 TCP over SSL test\r\n";
    ret = ssl_write_all((const uint8_t *)msg, strlen(msg));

    if (ret < 0) {
        close(SOCKET_SSL_CLIENT);

        while (1)
            ;
    }

    printf("TLS sent: %s", msg);

    while (1) {
        uint16_t recv_len = 0;
        uint8_t sock_status = getSn_SR(SOCKET_SSL_CLIENT);

        if (sock_status == SOCK_CLOSE_WAIT) {
            printf("Server closed connection\r\n");
            mbedtls_ssl_close_notify(&g_ssl);
            disconnect(SOCKET_SSL_CLIENT);
            close(SOCKET_SSL_CLIENT);

            while (1)
                ;
        }

        if (sock_status == SOCK_CLOSED) {
            printf("Socket closed\r\n");

            while (1)
                ;
        }

        getsockopt(SOCKET_SSL_CLIENT, SO_RECVBUF, &recv_len);

        if (recv_len > 0) {
            memset(g_ssl_buf, 0x00, sizeof(g_ssl_buf));

            ret = mbedtls_ssl_read(&g_ssl, g_ssl_buf, sizeof(g_ssl_buf) - 1);

            if (ret > 0) {
                printf("TLS received: %s\r\n", g_ssl_buf);
                ssl_write_all(g_ssl_buf, (size_t)ret);
                continue;
            }

            if ((ret == MBEDTLS_ERR_SSL_WANT_READ) ||
                (ret == MBEDTLS_ERR_SSL_WANT_WRITE) ||
                (ret == MBEDTLS_ERR_SSL_TIMEOUT)) {
                continue;
            }

            if (ret == 0) {
                printf("TLS peer closed connection\r\n");
            } else {
                print_mbedtls_error("mbedtls_ssl_read", ret);
            }

            close(SOCKET_SSL_CLIENT);

            while (1)
                ;
        }

        sleep_ms(1);
    }
}

7 运行结果与通信验证

完成硬件连接、工程编译和 UF2 烧录后,即可打开串口终端查看 TCP Client over SSL 示例运行结果,并在电脑端查看 SSL Server 接收情况。


7.1 VS Code 串口输出结果

串口终端输出如下:

---- 已打开串行端口 COM104 ----

=== WIZnet TCP client over SSL ===
Boot OK, initializing timer
Initializing WIZchip SPI
[PIO SPI CLOCK SPEED : 50.00 MHz] (sys=200.00 MHz)

Initializing WIZchip critical section
Resetting WIZchip
Initializing WIZchip, waiting for PHY link if needed
Checking WIZchip version
Applying network configuration
====================================================================================================
 W5500 network configuration : static

 MAC         : 00:08:DC:12:34:56
 IP          : 192.168.1.188
 Subnet Mask : 255.255.255.0
 Gateway     : 192.168.1.1
 DNS         : 8.8.8.8
====================================================================================================

TCP client over SSL example start
Target SSL server: 192.168.1.120:5000
Local socket port: 50000
Socket opened
Socket status after connect: 0x00
Connect failed: -4, retrying in 1 second
Socket opened
Socket status after connect: 0x00
Connect failed: -13, retrying in 1 second
Socket opened
Socket status after connect: 0x17
TCP connected
TLS handshake start
TLS handshake OK
Ciphersuite: TLS-DHE-RSA-WITH-AES-128-GCM-SHA256
TLS sent: W5x00 TCP over SSL test

从输出结果可以看到,开发板网络参数配置正常,当前静态 IP 为 192.168.1.188。程序前两次连接失败后会自动重试,当出现 TCP connectedTLS handshake OKTLS sent 时,说明 TCP over SSL 通信已经成功。


7.2 电脑端 OpenSSL Server 接收结果

电脑端 OpenSSL Server 连接成功后,可以看到 TLS 握手过程和开发板发送的数据内容。

实际运行效果如下图所示,左侧为 VS Code 串口输出结果,右侧为电脑端 OpenSSL Server 运行窗口。

在这里插入图片描述

从图中可以看到,电脑端 OpenSSL Server 已经完成 TLS 握手,并接收到开发板发送的内容:

W5x00 TCP over SSL test

这说明 W55RP20 已经成功完成 TCP over SSL 加密通信测试。


7.3 TCP Client over SSL 功能 GIF 演示

下面的 GIF 展示了 W55RP20 作为 TCP Client 连接电脑端 OpenSSL Server,完成 TLS 握手并发送测试数据的过程。

在这里插入图片描述


8 常见问题一站式排查指南

8.1 TCP 连接失败

问题现象 排查步骤
串口一直打印 Connect failed
1. 确认电脑端 OpenSSL Server 已经启动
2. 确认代码中的 g_ssl_server_ip 是电脑当前局域网 IP
3. 确认 PORT_SSL_SERVER 与电脑端监听端口一致
4. 确认电脑和开发板处于同一局域网
5. 临时关闭电脑防火墙后再次测试

8.2 TLS 握手失败

问题现象 排查步骤
串口打印 mbedtls_ssl_handshake failed
1. 确认电脑端运行的是 SSL/TLS Server,不是普通 TCP Server
2. 确认电脑端支持代码中配置的加密套件
3. 确认证书和密钥配置正常
4. 确认程序中已关闭证书校验用于本地测试
5. 重新启动 SSL Server 后再次连接

8.3 OpenSSL Server 没有收到数据

问题现象 排查步骤
TLS 握手成功,但电脑端没有看到测试字符串
1. 确认串口已经打印 TLS sent
2. 确认 SSL Server 接收窗口没有被清空或过滤
3. 确认 SSL Server 没有在握手后立即关闭连接
4. 确认电脑端工具支持显示收到的应用层数据
5. 重新启动 SSL Server 后再次测试

9 W55RP20 核心优势对比

为了让你更直观地了解 W55RP20 的价值,我们对比了目前主流的三种嵌入式以太网方案:

对比维度 W55RP20 集成方案 外接 PHY 芯片方案 外接串口转以太网模块方案
BOM 成本
(单芯片)
中高
(MCU + 模块 + 外围器件)
PCB 面积
(仅需网口电路)

(需预留芯片和布线空间)
开发难度
(官方 C 示例工程完善,可快速验证)
中高
(调试协议栈、编写驱动)
网络稳定性 极高
(WIZnet 专注硬件 TCP/IP 协议栈 25 年)
不定
(依赖研发人员对协议栈和网络调试的掌握程度)
不定
(视模块厂商能力水平)
CPU 资源占用
(TCP/IP 网络处理由硬件完成)
较高
(协议栈运行在 MCU 上,占用相关资源)
硬件 Socket 数量 8 个独立硬件 Socket 视 MCU 能力而定,理论支持多路扩展 一般为单路透传
网络吞吐量 最高 15Mbps 视 MCU 能力而定 约 3-5Mbps
接口易用性 单芯片集成 要 MCU 带有 MII/RMII 等接口 TTL 接口
部署难度
(官方 C 示例工程完善,可在基础例程上继续扩展)

(应用层协议需要手动移植开源库适配)
视模块集成情况,无集成的功能需要自我封包拆包

10 典型应用场景

W55RP20 TCP Client over SSL 示例适合以下应用场景:

  1. 设备加密连接云平台
  2. 工业设备安全数据上传
  3. 传感器数据加密传输
  4. 远程监控终端安全通信
  5. HTTPS / TLS 网关连接测试
  6. 安全日志上传

在这些场景中,开发板可以作为 TCP Client 主动连接服务器,并通过 TLS/SSL 加密通道发送数据。


11 系列预告与资源获取

11.1 系列预告

下一篇教程将继续讲解 W55RP20 C 语言开发中的 TCP Server 多 Socket 通信测试

与本文开发板作为 TCP Client 主动连接 OpenSSL Server 不同,下一篇将让开发板作为 TCP Server,同时监听多个端口,电脑端 SocketTester 作为 TCP Client 主动连接开发板,并完成 TCP 数据收发和回显测试。


11.2 资源获取

如果本文对你有帮助,欢迎点赞、收藏、关注,你的支持是我们持续更新的动力!

如有任何问题,欢迎在评论区留言,我们会第一时间回复。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐