stm32开发之threadx+netxduo(tcp 服务端使用记录)

本文介绍了如何在STM32F407ZGT6开发环境中使用ThreadX实现动态内存分配和管理,构建了一个TCP服务器,包括线程创建、数据包池、IP和TCP设置,以及处理客户端连接和数据接收的过程。
摘要由CSDN通过智能技术生成

前言

  1. 本篇需要用到threadx之动态内存的实现记录 里面的动态内存分配管理代码.
  2. 开发环境使用的stm32cubemx+clion组合
  3. 芯片使用的是stm32f407zgt6,网口使用的是lan8720,使用cubemx提供的lan8742也可以驱动,注意实际的网口与芯片的引脚

示例代码

tcp 服务端

/*
 * Copyright (c) 2024-2024,shchl
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2024-4-8     shchl   first version
 */
#include "includes.h"
#include "nx_tcp.h"


#define     TCP_SERVER_STACK_SIZE       4096  /*任务栈大小*/
#define     TCP_SERVER_PRIORITY         4     /*优先级*/
#define     PACKET_SIZE                 1536 /*数据包大小*/
#define     POOL_SIZE                   ((sizeof(NX_PACKET) + PACKET_SIZE) * 16) /*数据包池大小(16个缓冲区)*/
#define     TCP_SERVER_IP               IP_ADDRESS(192, 168, 8, 9) /*本地ip*/
#define     TCP_SERVER_LISTEN_PORT      8080 /*监听端口*/
TX_THREAD tcp_server_thread;            /*tcp 服务线程*/
NX_PACKET_POOL tcp_server_pool;         /*数据包池*/
NX_IP tcp_server_ip;                    /*服务端ip,即本地ip*/
NX_TCP_SOCKET tcp_server_socket;        /*服务端socket*/
static ULONG error_counter = 0;         /*错误计数器(可用来排查问题出现位置)*/

VOID thread_server_entry(ULONG thread_input);

void tcp_server_connect_received(NX_TCP_SOCKET *socket_ptr, UINT port);

void tcp_server_disconnect_received_callback(NX_TCP_SOCKET *socket);

static UINT receive_socket_handle(NX_TCP_SOCKET *socket);

static inline void status_check(UINT stat) {
    if (stat) {
        error_counter++;
        // todo 
    }
}
/**
 * @brief tcp 服务线程创建
 */
void app_task_tcp_server_thread_create() {
    tx_log("------------------app_task_tcp_server_thread_create start--------------------\r\n");
    UINT status;
    /* 创建server 线程.  */
    tx_thread_create(&tcp_server_thread,
                     "tcp sever thread",
                     thread_server_entry, 0,
                     app_malloc(TCP_SERVER_STACK_SIZE),
                     TCP_SERVER_STACK_SIZE,
                     TCP_SERVER_PRIORITY,
                     TCP_SERVER_PRIORITY,
                     TX_NO_TIME_SLICE,
                     TX_AUTO_START);
    /* 创建 packet 内存池(可以理解为分配一个大数组).  */
    status = nx_packet_pool_create(&tcp_server_pool,
                                   "server packet pool",
                                   PACKET_SIZE,
                                   app_malloc(POOL_SIZE),
                                   POOL_SIZE);

    status_check(status);

    /* 创建一个ip 实列. 内部会创建一个对应的线程 */
    status = nx_ip_create(&tcp_server_ip,
                          "tcp server instance",
                          TCP_SERVER_IP, 0xFFFFFF00UL, &tcp_server_pool,
                          nx_stm32_eth_driver,/*对应的网卡驱动*/
                          app_malloc(2048), /*ip 内存栈空间首地址*/
                          2048,
                          2);
    status_check(status);

    /* 开启 地址解析协议 并分配缓冲区. */
    status = nx_arp_enable(&tcp_server_ip, app_malloc(1024), 1024);
    status_check(status);

    /*开启 icmp 协议, 能通过ping 命令去检查ip */
    status = nxd_icmp_enable(&tcp_server_ip);
    status_check(status);


    /* 开启 tcp 处理 */
    status = nx_tcp_enable(&tcp_server_ip);
    tx_log("------------------app_task_tcp_server_thread_create finished--------------------\r\n");

}

VOID thread_server_entry(ULONG thread_input) {
    NX_PARAMETER_NOT_USED(thread_input);
    UINT status;

    ULONG actual_status;
    /* 确保 IP 实例已初始化。  */
    do {
        /* 等待 1 秒钟,让 内部 IP 线程完成其初始化。. */
        status = nx_ip_status_check(&tcp_server_ip,
                                    NX_IP_INITIALIZE_DONE,
                                    &actual_status,
                                    NX_IP_PERIODIC_RATE);
    } while (status != NX_SUCCESS);


    /* 创建socket */
    status = nx_tcp_socket_create(&tcp_server_ip,
                                  &tcp_server_socket,
                                  "Server Socket",
                                  NX_IP_NORMAL, /* IP服务类型 */
                                  NX_FRAGMENT_OKAY,/* 使能IP分段 */
                                  NX_IP_TIME_TO_LIVE, /*默认数据包生存时间*/
                                  PACKET_SIZE, /*这个参数对应到后面发送的数据包是否会进行分包处理*/
                                  NX_NULL, /* 用于在接收流中检测到紧急数据时调用的回调函数 */
                                  tcp_server_disconnect_received_callback  /* TCP Socket另一端发出断开连接时调用的回调函数 */
    );

    status_check(status);


    /*  监听新的链接。 */
    status = nx_tcp_server_socket_listen(&tcp_server_ip, /* IP实例控制块 */
                                         TCP_SERVER_LISTEN_PORT, /* 端口 */
                                         &tcp_server_socket,/* TCP Socket控制块 */
                                         5,/* 可以监听的连接数 */
                                         tcp_server_connect_received  /* 监听接收到连接函数 */
    );

    status_check(status);


    while (1) {

        /* 接受客户端套接字连接。(阻塞)  */
        status = nx_tcp_server_socket_accept(&tcp_server_socket, NX_WAIT_FOREVER);

        status_check(status);

        /*处理接收到的客户端连接*/
        receive_socket_handle(&tcp_server_socket);
        /* 断开服务器套接字。  */
        status = nx_tcp_socket_disconnect(&tcp_server_socket, NX_IP_PERIODIC_RATE);
        if (status) {
            error_counter++;
        }

        /* 解除Socket和服务器端口的绑定 */
        status = nx_tcp_server_socket_unaccept(&tcp_server_socket);
        status_check(status);


        /* 重新监听 */
        status = nx_tcp_server_socket_relisten(
                &tcp_server_ip,
                TCP_SERVER_LISTEN_PORT,
                &tcp_server_socket);


        status_check(status);

    }

}

void tcp_server_connect_received(NX_TCP_SOCKET *socket_ptr, UINT port) {


    /* Check for the proper socket and port.  */
    if ((socket_ptr != &tcp_server_socket) || (port != TCP_SERVER_LISTEN_PORT)) {
        error_counter++;
    } else {
        ULONG ipaddr = socket_ptr->nx_tcp_socket_connect_ip.nxd_ip_address.v4;
        tx_log("current ip: %lu.%lu.%lu.%lu:%d\r\n",
               (ipaddr >> 24) & 0xff, (ipaddr >> 16) & 0xff,
               (ipaddr >> 8) & 0xff, (ipaddr & 0xff), socket_ptr->nx_tcp_socket_port);
    }
}


void tcp_server_disconnect_received_callback(NX_TCP_SOCKET *socket) {

    /* Check for proper disconnected socket.  */
    if (socket != &tcp_server_socket) {
        error_counter++;
    }
}

/**
 * @brief 处理接收的socket
 * @param socket
 * @return
 */
static UINT receive_socket_handle(NX_TCP_SOCKET *socket) {
    UINT status;
    NX_PACKET *packet_ptr;

    continue_rec:
    /* Receive a TCP message from the socket.  */
    status = nx_tcp_socket_receive(&tcp_server_socket, &packet_ptr, NX_IP_PERIODIC_RATE);
    /* Check for error.  */
    switch (status) {
        case NX_SUCCESS: {
            *packet_ptr->nx_packet_append_ptr = '\0'; /*在结尾处添加结束标志位,这里可以这样处理,前提是确保指向的位置可以被修改*/
            tx_log("data packet size:%d\r\n", packet_ptr->nx_packet_length);
            tx_log("nx_tcp_socket_receive data:%s\r\n", packet_ptr->nx_packet_prepend_ptr);

            /*处理完成之后释放数据包,这里需要我们自己进行释放*/
            nx_packet_release(packet_ptr);
            goto continue_rec; /*继续接收客户端*/
        }
        case NX_NO_PACKET: { /*客户端连接,但没有发送数据,这里可以做超时断开客户端连接操作*/
            // tx_log("nx_tcp_socket_receive NX_NO_PACKET\r\n");
            goto continue_rec; /*继续接收客户端*/
        }
        case NX_NOT_CONNECTED: {
            tx_log("client disconnected\r\n");
            break;
        }
        default: {
            tx_log("nx_tcp_socket_receive status:%d\r\n", status);
            break;
        }
    }
    return NX_SUCCESS;
}

调用主逻辑

在这里插入图片描述

结果

在这里插入图片描述
在这里插入图片描述

发送超过数据包大小

在这里插入图片描述

wireshark 数据查看

在这里插入图片描述

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

詩不诉卿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值