zoukankan      html  css  js  c++  java
  • 【STM32F407】第9章 ThreadX NetXDUO之TCP客户端

    最新教程下载:http://www.armbbs.cn/forum.php?mod=viewthread&tid=104619

    第9章   ThreadX NetXDUO之TCP客户端

    本章节为大家讲解NetXDUO的TCP客户端实现,学习本章节前,务必要优先学习第7章TCP传输控制协议基础知识。有了这些基础知识之后,再搞本章节会有事半功倍的效果。

    9.1 初学者重要提示

    9.2 TCP客户端API函数

    9.3 TCP客户端的实现方法

    9.4 网络调试助手和板子的调试操作步骤

    9.5 实验例程说明

    9.6 总结

    9.1   初学者重要提示

      1、学习本章节前,务必保证已经学习了第7章的基础知识。

      2、本章要掌握的函数稍多,可以先学会基本的使用,然后再深入了解这些函数使用时的注意事项,争取达到熟练使用。

      3、socket和监听的关系:

    •   创建的一个socket只能创建一个监听。
    •   创建的一个socket不能够监听多个 。
    •   创建多个socket可以创建多个监听。
    •   创建多个socket可以仅创建一个监听。

      4、ThreadX NetXDUO的TCP Socket数据包申请和释放问题

    •   函数nx_tcp_socket_receive 会申请一个NX_PACKET数据包用于接收,如果用户不使用了必须使用函数nx_packet_release释放。
    •   使用函数nx_tcp_socket_send必须有申请好的NX_PACKET数据包,可以使用函数nx_packet_allocate申请,也可以使用nx_tcp_socket_receive申请的。

    特别要注意的地方来了,函数nx_tcp_socket_send调用后会释放nx_packet_allocate或者nx_tcp_socket_receive申请的数据包。无需用户再去调用函数nx_packet_release释放。

    9.2   TCP客户端API函数

    下面一张图说明ThreadX NetXDUO TCP Socket的各种API玩法:

     

    9.2.1  函数nx_system_initialize

    函数原型:

    VOID nx_system_initialize(VOID); 

    函数描述:

    NetXDUO初始化,所有其它功能调用之前必须优先调用此函数。

    9.2.2  函数nx_packet_pool_create

    函数原型:

    UINT nx_packet_pool_create(
                              NX_PACKET_POOL *pool_ptr,
                              CHAR *name,
                              ULONG payload_size,
                              VOID *memory_ptr,
                              ULONG memory_size);                 

    函数描述:

    此函数用于数据包内存池创建

    函数参数:

    1.   第1个参数是内存池控制块的地址。
    2.   第2个参数是内存池名字。
    3.   第3个参数是内存池中每个数据包的字节数。 此值必须至少为 40 个字节,并且还必须可以被 4 整除。
    4.   第4个参数是内存池中数据地址,此地址必须ULONG对齐。
    5.   第5个参数是内存池大小。
    6.   返回值:
    •   NX_SUCCESS:(0x00) 创建内存池成功。
    •   NX_PTR_ERROR:(0x07) 第1个参数地址无效。
    •   NX_SIZE_ERROR:(0x09) 第5个参数内存池大小无效。
    •   NX_CALLER_ERROR:(0x11) 此服务的调用方无效。

    使用举例:

      /* 创建内存池 */
        status =  nx_packet_pool_create(&pool_0,                 /* 内存池控制块 */
                                        "NetX Main Packet Pool",/* 内存池名 */
                   1536, /* 内存池每个数据包大小,单位字节此值必须至少为 40 个字节,并且还必须可以被 4 整除 */
                 (ULONG*)(((int)packet_pool_area + 15) & ~15) ,/* 内存池地址,此地址必须ULONG对齐 */
                   NX_PACKET_POOL_SIZE);                        /* 内存池大小 */       

    9.2.3  函数nx_ip_create

    函数原型:

    UINT nx_ip_create(
        NX_IP *ip_ptr, 
        CHAR *name, ULONG ip_address,
        ULONG network_mask, 
        NX_PACKET_POOL *default_pool,
        VOID (*ip_network_driver)(NX_IP_DRIVER *),
        VOID *memory_ptr, 
        ULONG memory_size,
        UINT priority);                    

    函数描述:

    此函数使用用户提供的 IP 地址,数据包内存内存池和网络驱动程序创建 IP 实例。注意,直到 IP任务执行之后,才会调用网络驱动。

    函数参数:

    1.   第1个参数是创建IP实例的控制块指针。
    2.   第2个参数是IP实例的名字。
    3.   第3个参数是IP地址。
    4.   第4个参数是子网掩码
    5.   第5个参数是内存池地址。
    6.   第6个参数是网卡驱动地址。
    7.   第7个参数是IP任务栈地址
    8.   第8个参数是IP任务栈大小,单位字节。
    9.   第9个参数是IP任务优先级。
    10.   返回值
    •   NX_SUCCESS:(0x00) 创建 IP 实例成功。
    •   NX_NOT_IMPLEMENTED:(0x4A) 未正确配置 NetX Duo 库。
    •   NX_PTR_ERROR:(0x07) IP控制块地址、网络驱动函数指针、内存池地址或任务栈地址无效。
    •   NX_SIZE_ERROR:(0x09) 提供的任务栈大小太小。
    •   NX_CALLER_ERROR:(0x11) 此服务的调用方无效。
    •   NX_IP_ADDRESS_ERROR:(0x21) 提供的 IP 地址无效。
    •   NX_OPTION_ERROR:(0x21) 提供的 IP 任务优先级无效。

    使用举例:

    /* 例化IP */
        status = nx_ip_create(&ip_0,                                                   /* IP实例控制块 */                                    
                                "NetX IP Instance 0",                                  /* IP实例名 */     
                                IP_ADDRESS(IP_ADDR0, IP_ADDR1, IP_ADDR2, IP_ADDR3),    /* IP地址 */
                                0xFFFFFF00UL,                                          /* 子网掩码 */
                                &pool_0,                                               /* 内存池 */
                            nx_driver_stm32h7xx,                                   /* 网卡驱动 */
                                (UCHAR*)AppTaskNetXStk,                                /* IP任务栈地址 */
                                sizeof(AppTaskNetXStk),                             /* IP任务栈大小,单位字节 */
                                APP_CFG_TASK_NETX_PRIO);                            /* IP任务优先级 */

    9.2.4  函数nx_arp_enable

    函数原型:

    UINT nx_arp_enable(
        NX_IP *ip_ptr, 
        VOID *arp_cache_memory,
        ULONG arp_cache_size);                

    函数描述:

    此函数用于使能ARP地址解析。

    函数参数:

    1.   ip_ptr:IP实例地址。
    2.   arp_cache_memory:ARP缓存地址。
    3.   arp_cache_size:每个 ARP 条目均为 52 个字节,因此,ARP 条目总数是52字节整数倍。
    4.   返回值
    •   NX_SUCCESS:(0x00) 启用 ARP 成功。
    •   NX_PTR_ERROR:(0x07) IP实例地址或ARP缓存地址无效。
    •   NX_SIZE_ERROR:(0x09) 用户提供的 ARP 缓存内存太小。
    •   NX_CALLER_ERROR:(0x11) 此服务的调用方无效。
    •   NX_ALREADY_ENABLED:(0x15) 此组件已启用。

    使用举例:

    int32_t tcp_sock;
    
    tcp_sock = netTCP_GetSocket (tcp_cb_server);
        
    if (tcp_sock > 0) 
    {
    res = netTCP_Listen (tcp_sock, PORT_NUM);
    }
    
    if(netTCP_SendReady(tcp_sock) == true )
    {
    
    }

    9.2.5  函数nx_ip_fragment_enable

    函数原型:

    UINT nx_ip_fragment_enable(NX_IP *ip_ptr);

    函数描述:

    此函数用于启用 IPv4 和 IPv6 数据包分段和重组功能。创建 IP 任务时,此服务会自动禁用。

    函数参数:

      1、第1个参数是IP实例地址。

      2、返回值

    •   NX_SUCCESS:(0x00) 启用 IP 分段成功。
    •   NX_PTR_ERROR:(0x07) IP 实例地址无效。
    •   NX_CALLER_ERROR:(0x11) 此服务的调用方无效。
    •   NX_NOT_ENABLED:(0x14) IP 分段功能未编译到 NetX Duo 中。

    使用举例:

        /* 使能fragment */    
    status = nx_ip_fragment_enable(&ip_0);

    9.2.6  函数nx_tcp_enable

    函数原型:

    UINT nx_tcp_enable(NX_IP *ip_ptr);  

    函数描述:

    此函数用于使能TCP组件。

    函数参数:

      1、第1个参数是IP实例地址。

      2、返回值

    •   NX_SUCCESS:(0x00) 启用 TCP 成功。
    •   NX_ALREADY_ENABLED:(0x15) TCP 已启用。
    •   NX_PTR_ERROR:(0x07) IP 实例地址无效。
    •   NX_CALLER_ERROR:(0x11) 此服务的调用方无效。

    使用举例:

        /* 使能TCP */
        status =  nx_tcp_enable(&ip_0);

    9.2.7  函数nx_tcp_client_socket_bind

    函数原型:

    UINT nx_tcp_client_socket_bind(
        NX_TCP_SOCKET *socket_ptr,
        UINT port,
        ULONG wait_option);

    函数描述:

    此函数用于为创建的TCP Socket绑定端口。如果设置的端口号还不可用,可以设置等待时间。

    函数参数:

      1、第1个参数是TCP Socket指针。

      2、第2个参数是绑定的端口,范围1 -65535。如果设置为NX_ANY_PORT(0x0000),则会搜索一个可用端口号。

      3、第3个参数是端口号不可用时,等待时间定义:

    •   NX_NO_WAIT (0x00000000)
    •   NX_WAIT_FOREVER (0xFFFFFFFF)
    •   等待时间:(0x00000001 到 0xFFFFFFFE),单位是ThreadX系统时钟节拍。

      4、返回值

    •   NX_SUCCESS:(0x00) 绑定TCP Socket成功。
    •   NX_ALREADY_BOUND:(0x22) 此TCP Socket已与另一 TCP 端口绑定。
    •   NX_PORT_UNAVAILABLE:(0x23) 端口已与其他Socket绑定。
    •   NX_NO_FREE_PORTS:(0x45) 没有可用的端口。
    •   NX_WAIT_ABORTED:(0x1A) 已通过调用 tx_thread_wait_abort 中止请求。
    •   NX_INVALID_PORT:(0x46) 端口无效。
    •   NX_PTR_ERROR:(0x07) Socket指针无效。
    •   NX_CALLER_ERROR:(0x11) 此服务的调用方无效。
    •   NX_NOT_ENABLED:(0x14) 此组件尚未启用。

    使用举例:

        /* 绑定端口 */
        ret =  nx_tcp_client_socket_bind(&TCPSocket, DEFAULT_PORT, NX_WAIT_FOREVER);
    
        if (ret != NX_SUCCESS)
        {
            Error_Handler(__FILE__, __LINE__);   
    }

    9.2.8  函数nx_tcp_client_socket_unbind

    函数原型:

    UINT nx_tcp_client_socket_unbind(NX_TCP_SOCKET *socket_ptr);

    函数描述:

    此函数用于解除TCP Socket绑定的端口。

    函数参数:

    1、  第1个参数是TCP Socket指针。

    2、  返回值

    •   NX_SUCCESS:(0x00) 取消绑定Socket成功。
    •   NX_NOT_BOUND:(0x24) Socket未与任何端口绑定。
    •   NX_NOT_CLOSED:(0x35) Socket尚未断开连接。
    •   NX_PTR_ERROR:(0x07) Socket指针无效。
    •   NX_CALLER_ERROR:(0x11) 此服务的调用方无效。
    •   NX_NOT_ENABLED (0x14) 尚未启用此组件。

    使用举例:

        /* 绑定端口 */
        ret =  nx_tcp_client_socket_bind(&TCPSocket, DEFAULT_PORT, NX_WAIT_FOREVER);
    
        if (ret != NX_SUCCESS)
        {
            Error_Handler(__FILE__, __LINE__);   
        }

    9.2.9  函数nx_tcp_client_socket_connect

    函数原型:

    UINT nx_tcp_client_socket_connect(
        NX_TCP_SOCKET *socket_ptr,
        ULONG server_ip,
        UINT server_port,
        ULONG wait_option);

    函数描述:

    此函数用于创建TCP客户端连接远程服务器。

    函数参数:

    1、  第1个参数是TCP Socket指针。

    2、  第2个参数是远程服务器IP。

    3、  第3个参数是远程端口。

    4、  第4个参数连接远程服务器的等待选项,支持的参数如下:

    •   NX_NO_WAIT (0x00000000)
    •   NX_WAIT_FOREVER (0xFFFFFFFF)
    •   等待时间:(0x00000001 到 0xFFFFFFFE),单位是ThreadX系统时钟节拍。

    5、  返回值:

    •   NX_SUCCESS:(0x00) 连接套接字成功。
    •   NX_NOT_BOUND:(0x24) 套接字未绑定。
    •   NX_NOT_CLOSED:(0x35) 套接字未处于关闭状态。
    •   NX_IN_PROGRESS (0x37) 未指定等待,正在尝试连接。
    •   NX_INVALID_INTERFACE:(0x4C) 提供了无效的接口。
    •   NX_WAIT_ABORTED:(0x1A) 已通过调用 tx_thread_wait_abort 中止所请求的挂起。
    •   NX_IP_ADDRESS_ERROR:(0x21) 服务器 IP 地址无效。
    •   NX_INVALID_PORT (0x46) 端口无效。
    •   NX_PTR_ERROR:(0x07) 套接字指针无效。
    •   NX_CALLER_ERROR:(0x11) 此服务的调用方无效。
    •   NX_NOT_ENABLED (0x14) 尚未启用此组件。

    使用举例:

        /* 连接远程服务器 */
        ret = nx_tcp_client_socket_connect(&TCPSocket, TCP_SERVER_ADDRESS, TCP_SERVER_PORT, NX_WAIT_FOREVER);
    
        if (ret != NX_SUCCESS)
        {
            Error_Handler(__FILE__, __LINE__);   
        }

    9.2.10        函数nx_tcp_socket_info_get

    函数原型:

    UINT nx_tcp_socket_info_get(
        NX_TCP_SOCKET *socket_ptr,
        ULONG *tcp_packets_sent,
        ULONG *tcp_bytes_sent,
        ULONG *tcp_packets_received,
        ULONG *tcp_bytes_received,
        ULONG *tcp_retransmit_packets,
        ULONG *tcp_packets_queued,
        ULONG *tcp_checksum_errors,
        ULONG *tcp_socket_state,
        ULONG *tcp_transmit_queue_depth,
        ULONG *tcp_transmit_window,
        ULONG *tcp_receive_window);

    函数描述:

    用于获取TCP Socket相关信息。

    函数参数:

    1.   第1个参数是TCP Socket指针。
    2.   第2个参数是发送的TCP数据包总数目。
    3.   第3个参数是发送的TCP总字节数。
    4.   第4个参数是接收的TCP数据包总数目。
    5.   第5个参数是接收的TCP总字节数。
    6.   第6个参数是重新传输的TCP数据包总数目。
    7.   第7个参数是Socket上TCP排队的TCP数据包总数。
    8.   第8个参数是Socket上有校验和错误的TCP数据包总数。
    9.   第9个参数是Socket当前状态。
    10.   第10个参数是仍在排队等待ACK的发送数据包总数。
    11.   第11个参数是当前发送窗口大小。
    12.   第12个参数是当前接收窗口大小。
    13.   返回值:
    •   NX_SUCCESS:(0x00) 检索 TCP Socket信息成功。
    •   NX_PTR_ERROR:(0x07)  Socket指针无效。
    •   NX_CALLER_ERROR:(0x11) 此服务的调用方无效。
    •   NX_NOT_ENABLED:(0x14) 此组件尚未启用。

    使用举例:

       /* 获取socket状态 */
            nx_tcp_socket_info_get(&TCPSocket,     /* TCP Socket控制块 */
                                   NULL,           /* 发送的TCP数据包总数目 */
                                   NULL,           /* 发送的TCP总字节数 */
                                   NULL,           /* 接收TCP数据包总数目 */
                                   NULL,           /* 接收的TCP总字节数 */
                                   NULL,           /* 重新传输的TCP数据包总数目 */
                                   NULL,           /* Socket上TCP排队的TCP数据包总数 */
                                   NULL,           /* Socket上有校验和错误的TCP数据包总数 */
                                   &socket_state,  /* Socket当前状态 */
                                   NULL,           /* 仍在排队等待ACK的发送数据包总数 */
                                   NULL,           /* 当前发送窗口大小 */
                                   NULL);          /* 当前接收窗口大小 */

    9.2.11        函数nx_tcp_socket_receive

    函数原型:

    UINT nx_tcp_socket_receive(
        NX_TCP_SOCKET *socket_ptr,
        NX_PACKET **packet_ptr,
        ULONG wait_option);

    函数描述:

    此函数用于从指定的Socket接收TCP数据,如果指定的Socket上没有已经排队的数据,则调用方会根据提供的等待选项参数挂起。

    函数参数:

    1、  第1个参数是TCP Socket指针

    2、  第2个参数是TCP数据包指针。

    3、  第3个参数是Socket队列上没有数据时的处理:

    •   NX_NO_WAIT (0x00000000)。
    •   NX_WAIT_FOREVER (0xFFFFFFFF)。
    •   以时钟周期为单位的超时值(0x00000001 到 0xFFFFFFFE)。

    4、  返回值:

    •   NX_SUCCESS:(0x00) 接收Socket数据成功。
    •   NX_NOT_BOUND:(0x24) Socket未绑定。
    •   NX_NO_PACKET:(0x01) 未收到任何数据。
    •   NX_WAIT_ABORTED:(0x1A) 通过调用 tx_thread_wait_abort 中止挂起。
    •   NX_NOT_CONNECTED:(0x38) 该Socket不再处于已连接状态。
    •   NX_PTR_ERROR:(0x07) Socket指针或返回数据包指针无效。
    •   NX_CALLER_ERROR:(0x11) 此服务的调用方无效。
    •   NX_NOT_ENABLED:(0x14) 此组件尚未启用。

    注意事项:

    1. 如果返回了 NX_SUCCESS,则应用程序负责:不再需要收到数据包时将其释放。

    使用举例:

    /* 接收TCP客户端发的TCP数据包 */
    ret = nx_tcp_socket_receive(&TCPSocket,        /* TCP Socket控制块 */
                              &data_packet,      /* 接收到的数据包 */
                               NX_WAIT_FOREVER);  /* 永久等待 */

    9.2.12        函数nx_tcp_socket_send

    函数原型:

    UINT nx_tcp_socket_send(
        NX_TCP_SOCKET *socket_ptr,
        NX_PACKET *packet_ptr,
        ULONG wait_option);

    函数描述:

    此函数用于TCP Socket数据发送。 如果接收方最近一次建议的窗口大小低于此请求,则此函数可以根据指定的等待参数挂起。此函数可保证不会将大于 MSS 的数据包数据发送到 IP 层。

    函数参数:

    1、  第1个参数是TCP Socket句柄。

    2、  第2个参数是TCP数据包指针。

    3、  第3个参数是发送的数据包大于接收方窗口大小时的参数处理,支持如下参数:

    •   NX_NO_WAIT (0x00000000)
    •   NX_WAIT_FOREVER (0xFFFFFFFF)
    •   以时钟周期为单位的超时值(0x00000001 到 0xFFFFFFFE)

    4、  返回值,返回以下几种状态值:

    •   NX_SUCCESS:(0x00) Socket发送成功。
    •   NX_NOT_BOUND:(0x24) Socket未与任何端口绑定。
    •   NX_NO_INTERFACE_ADDRESS:(0x50) 找不到合适的传出接口。
    •   NX_NOT_CONNECTED:(0x38) 套接字不再处于已连接状态。
    •   NX_WINDOW_OVERFLOW:(0x39) 请求大于接收方所播发的窗口大小(以字节为单位)。
    •   NX_WAIT_ABORTED:(0x1A) 已通过调用 tx_thread_wait_abort 中止所请求的挂起。
    •   NX_INVALID_PACKET:(0x12) 数据包未分配。
    •   NX_TX_QUEUE_DEPTH:(0x49) 已达到最大传输队列深度。
    •   NX_OVERFLOW:(0x03) 数据包追加指针无效。
    •   NX_PTR_ERROR:(0x07) 套接字指针无效。
    •   NX_CALLER_ERROR:(0x11) 此服务的调用方无效。
    •   NX_NOT_ENABLED:(0x14) 此组件尚未启用。
    •   NX_UNDERFLOW:(0x02) 数据包前置指针无效。

    注意事项:

    1. 除非返回了错误,否则应用程序不应在调用此函数后释放该数据包。这样做会导致不可预知的结果,因为网络驱动程序还会在传输后尝试释放该数据包。

    使用举例:

    /* 立即将接收到的数据发送回去 */
    ret =  nx_tcp_socket_send(&TCPSocket,       /* TCP Socket控制块 */
                            data_packet,      /* 数据包 */
                            NX_WAIT_FOREVER); /* 永久等待 */

    9.2.13        函数nx_packet_data_retrieve

    函数原型:                                 

    UINT nx_packet_data_retrieve(
        NX_PACKET *packet_ptr,
        VOID *buffer_start,
        ULONG *bytes_copied);

    函数描述:

    此函数用于将提供的数据包中的数据复制到提供的缓冲区。复制的实际字节数由形参bytes_copied 所指向的存储单元返回。

    注意,此函数不会更改该数据包的内部状态。检索的数据仍存在于该数据包中。

    函数参数:

    1、  第1个参数是指向源数据包的指针

    2、  第2个参数是目的数据包的地址。

    3、  第3个参数是最终复制的字节数存储地址。

    4、  返回值,返回以下几种状态值:

    •   NX_SUCCESS:(0x00)复制数据包数据成功。
    •   NX_INVALID_PACKET:(0x12) 数据包无效。
    •   NX_PTR_ERROR:(0x07) 形参地址无效。

    注意事项:

    目标缓冲区的大小必须足以容纳该数据包的内容。否则内存会损坏,导致不可预知的结果。

    使用举例:

    /* 获取客户端发来的数据 */
     nx_packet_data_retrieve(data_packet,    /* 接收到的数据包 */
                          data_buffer,    /* 解析出数据 */
                          &bytes_read);   /* 数据大小 */

    9.2.14        函数nx_packet_release

    函数原型:

    UINT nx_packet_release(NX_PACKET *packet_ptr);

    函数描述:

    此函数用于释放数据包,包括链接到指定数据包的任何其他数据包。如果有其他任务在等待这个数据包,则该任务会获得该数据包并继续执行。

    函数参数:

    1、  第1个参数是数据包地址。

    2、  返回值,返回以下几种状态值:

    •   NX_SUCCESS:(0x00) 释放数据包成功。
    •   NX_PTR_ERROR:(0x07) 数据包指针无效。
    •   NX_UNDERFLOW:(0x02) 预置指针小于有效负载开始位置。
    •   NX_OVERFLOW:(0x03) 追加指针大于有效负载结束位置。

    注意事项:

    应用程序必须防止多次释放同一数据包,否则会导致不可预知的结果。

    9.2.15        函数nx_tcp_socket_disconnect

    函数原型:

    UINT nx_tcp_socket_disconnect(
                     NX_TCP_SOCKET *socket_ptr,
                     ULONG wait_option);

    函数描述:

    此函数用于断开已建立的客户端或服务器Socket。在服务器Socket断开连接后应该有一个取消接受请求,而断开连接的客户端Socket会处于准备好接受其他连接请求的状态。 如果断开连接过程无法立即完成,则该函数会根据提供的等待选项挂起。

    函数参数:

    1、  第1个参数是TCP Socket地址。

    2、  第2个参数等待断开连接时,支持的参数:

    •   NX_NO_WAIT (0x00000000)。
    •   NX_WAIT_FOREVER (0xFFFFFFFF)。
    •   以时钟周期为单位的超时值(0x00000001 到 0xFFFFFFFE)。

    3、  返回值,返回以下几种状态值:

    •   NX_SUCCESS:(0x00) 断开Socket连接成功。
    •   NX_NOT_CONNECTED:(0x38) 指定的Socket未连接。
    •   NX_IN_PROGRESS:(0x37) 断开连接正在进行。
    •   NX_WAIT_ABORTED:(0x1A) 已通过调用 tx_thread_wait_abort 中止挂起请求。
    •   NX_PTR_ERROR:(0x07) Socket指针无效。
    •   NX_CALLER_ERROR:(0x11) 此服务的调用方无效。
    •   NX_NOT_ENABLED:(0x14) 此组件尚未启用。

    使用举例:

    /* 断开连接 */
    nx_tcp_socket_disconnect(&TCPSocket, 
                            NX_WAIT_FOREVER);

    9.3   TCP客户端的实现方法

    9.3.1  NetXDUO初始化

    创建TCP服务器前,要初始化NetX,创建内存池,例化IP:

    /*
    *********************************************************************************************************
    *    函 数 名: NetXTest
    *    功能说明: TCPnet应用
    *    形    参: 无
    *    返 回 值: 无
    *********************************************************************************************************
    */    
    void NetXTest(void)
    {
        UINT status;
        UINT ret;
        ULONG socket_state;
        UINT old_priority;
    
        NX_PACKET *data_packet;
        ULONG bytes_read;
        
        ULONG peer_ip_address;
        ULONG peer_port;
        
        
        /* 初始化NetX */
        nx_system_initialize();
    
        /* 创建内存池 */
        status =  nx_packet_pool_create(&pool_0,                 /* 内存池控制块 */
                                         "NetX Main Packet Pool",/* 内存池名 */
                   1536, /* 内存池每个数据包大小,单位字节此值必须至少为 40 个字节,并且还必须可以被 4 整除 */
                 (ULONG*)(((int)packet_pool_area + 15) & ~15) ,/* 内存池地址,此地址必须ULONG对齐 */
                   NX_PACKET_POOL_SIZE);                        /* 内存池大小 */                  
              
        /* 检测创建是否失败 */
        if (status) error_counter++;
    
        /* 例化IP */
        status = nx_ip_create(&ip_0,                                                   /* IP实例控制块 */                                    
                                "NetX IP Instance 0",                                  /* IP实例名 */     
                                IP_ADDRESS(IP_ADDR0, IP_ADDR1, IP_ADDR2, IP_ADDR3),    /* IP地址 */
                                0xFFFFFF00UL,                                          /* 子网掩码 */
                                &pool_0,                                               /* 内存池 */
                            nx_driver_stm32h7xx,                                   /* 网卡驱动 */
                                (UCHAR*)AppTaskNetXStk,                                /* IP任务栈地址 */
                                sizeof(AppTaskNetXStk),                             /* IP任务栈大小,单位字节 */
                                APP_CFG_TASK_NETX_PRIO);                            /* IP任务优先级 */
                                
                
        /* 检测创建是否失败 */
        if (status) error_counter++;
    
        /* 使能ARP,并提供ARP缓存 */
        status =  nx_arp_enable(&ip_0,               /* IP实例控制块 */
                         (void *)arp_space_area,  /* ARP缓存地址 */
             sizeof(arp_space_area));   /* 每个 ARP 条目均为 52 个字节,因此,ARP 条目总数是52字节整数倍 */
    
        /* 使能fragment */    
        status = nx_ip_fragment_enable(&ip_0);
    
        /* 检测使能成功 */
        if (status) error_counter++;
    
        /* 使能TCP */
        status =  nx_tcp_enable(&ip_0);
    
        /* 检测使能成功 */
        if (status) error_counter++;
    
        /* 使能UDP  */
        status =  nx_udp_enable(&ip_0);
    
        /* 检测使能成功 */
        if (status) error_counter++;
    
        /* 使能ICMP */
        status =  nx_icmp_enable(&ip_0);
    
        /* 检测使能成功 */
        if (status) error_counter++;   
        
        /* NETX初始化完毕后,重新设置优先级 */
        tx_thread_priority_change(netx_thread_ptr, APP_CFG_TASK_NETX_PRIO1, &old_priority);
    tx_thread_priority_change(&AppTaskNetXProTCB, APP_CFG_TASK_NetXPro_PRIO1, &old_priority);
    
        /* 省略 */
    }

    程序末尾务优先级做了特别处理,创建的时候先设置为低优先级,检测到网线正常连接并初始了网络后将优先级设置到正常水平。

    9.3.2  TCP客户端实现

    下面是创建TCP客户端并远程连接服务器:

    /*
    *********************************************************************************************************
    *    函 数 名: NetXTest
    *    功能说明: TCPnet应用
    *    形    参: 无
    *    返 回 值: 无
    *********************************************************************************************************
    */    
    void NetXTest(void)
    {
      
         /* 省略 */
    
        /* 创建TCP Socket */
        ret = nx_tcp_socket_create(&ip_0,                 /* IP实例控制块 */    
                                   &TCPSocket,            /* TCP控制块 */ 
                                   "TCP Server Socket",   /* TCP Socket名 */ 
                                   NX_IP_NORMAL,          /* IP服务类型 */ 
                                   NX_FRAGMENT_OKAY,      /* 使能IP分段 */ 
                                   NX_IP_TIME_TO_LIVE,    /*用于定义此数据包在被丢弃之前可通过的路由器数目 */ 
                                   4320,                  /* TCP Socket接收队列中允许的最大字节数 */ 
                                   NX_NULL,               /* 用于在接收流中检测到紧急数据时调用的回调函数 */
                                   NX_NULL);              /* TCP Socket另一端发出断开连接时调用的回调函数 */
        if (ret)
        {
            Error_Handler(__FILE__, __LINE__);    
        }
    
        /*
        * 监听新的链接。
        * 创建回调TCP_listen_callback表示监听到新连接。
        */
        ret = nx_tcp_server_socket_listen(&ip_0,                  /* IP实例控制块 */  
                                      DEFAULT_PORT,           /* 默认端口 */          
                                          &TCPSocket,             /* TCP Socket控制块 */
                                          MAX_TCP_CLIENTS,        /* 可以监听的连接数 */
                                          NULL);                  /* 监听回调函数 */
    
        if (ret)
        {
            Error_Handler(__FILE__, __LINE__);
        }
    
        /* 启动TCP通信前,接收新连接 */
        ret = nx_tcp_server_socket_accept(&TCPSocket,         /* TCP Socket控制块 */
                                           TX_WAIT_FOREVER);  /* 监听回调函数 */
    
        if (ret)
        {
            Error_Handler(__FILE__, __LINE__);
        }
    
        /* 绑定端口 */
        ret =  nx_tcp_client_socket_bind(&TCPSocket, DEFAULT_PORT, NX_WAIT_FOREVER);
    
        if (ret != NX_SUCCESS)
        {
            Error_Handler(__FILE__, __LINE__);   
        }
    
        /* 连接远程服务器 */
        ret = nx_tcp_client_socket_connect(&TCPSocket, TCP_SERVER_ADDRESS, TCP_SERVER_PORT, NX_WAIT_FOREVER);
    
        if (ret != NX_SUCCESS)
        {
            Error_Handler(__FILE__, __LINE__);   
        }
    
         /* 省略 */
    }

    9.3.3  TCP回环通信实现

    回环的意思就是电脑端网络助手发送数据给板子后,板子再将数据返回。为了方便大家使用,本例子将接收数据包和发送数据包分别做了定义:

    /*
    *********************************************************************************************************
    *    函 数 名: NetXTest
    *    功能说明: TCPnet应用
    *    形    参: 无
    *    返 回 值: 无
    *********************************************************************************************************
    */    
    void NetXTest(void)
    {
      
         /* 省略 */
    
    while(1)
        {
            TX_MEMSET(data_buffer, '\0', sizeof(data_buffer));
    
            /* 获取socket状态 */
            nx_tcp_socket_info_get(&TCPSocket,     /* TCP Socket控制块 */
                                   NULL,           /* 发送的TCP数据包总数目 */
                                   NULL,           /* 发送的TCP总字节数 */
                                   NULL,           /* 接收TCP数据包总数目 */
                                   NULL,           /* 接收的TCP总字节数 */
                                   NULL,           /* 重新传输的TCP数据包总数目 */
                                   NULL,           /* Socket上TCP排队的TCP数据包总数 */
                                   NULL,           /* Socket上有校验和错误的TCP数据包总数 */
                                   &socket_state,  /* Socket当前状态 */
                                   NULL,           /* 仍在排队等待ACK的发送数据包总数 */
                                   NULL,           /* 当前发送窗口大小 */
                                   NULL);          /* 当前接收窗口大小 */
    
            /* 如果连接还没有建立,继续接受新连接,成功的话开启接收数据 */
            if(socket_state != NX_TCP_ESTABLISHED)
            {
                /* 绑定端口 */
                ret =  nx_tcp_client_socket_bind(&TCPSocket, DEFAULT_PORT, NX_WAIT_FOREVER);
    
                if (ret != NX_SUCCESS)
                {
                    Error_Handler(__FILE__, __LINE__);   
                }
    
                /* 连接远程服务器 */
                ret = nx_tcp_client_socket_connect(&TCPSocket, TCP_SERVER_ADDRESS, 
    TCP_SERVER_PORT, NX_WAIT_FOREVER);
    
                if (ret != NX_SUCCESS)
                {
                    Error_Handler(__FILE__, __LINE__);   
                }
            }
            
            if((socket_state == NX_TCP_ESTABLISHED)&&(ret == NX_SUCCESS))
            {
                    
                /* 接收TCP客户端发的TCP数据包 */
                ret = nx_tcp_socket_receive(&TCPSocket,        /* TCP Socket控制块 */
                                            &RecPacket,      /* 接收到的数据包 */
                                            NX_WAIT_FOREVER);  /* 永久等待 */
    
                if (ret == NX_SUCCESS)
                {
                    
                    /* 获取客户端的IP地址和端口 */
                    nx_tcp_socket_peer_info_get(&TCPSocket,       /* TCP Socket控制块 */ 
                                                &peer_ip_address, /* 远程IP地址 */ 
                                                &peer_port);      /* 远程端口号 */
    
                    /* 获取客户端发来的数据 */
                    nx_packet_data_retrieve(RecPacket,    /* 接收到的数据包 */
                                            data_buffer,    /* 解析出数据 */
                                            &bytes_read);   /* 数据大小 */
    
                    /* 打印接收到数据 */
                    PRINT_DATA(peer_ip_address, (unsigned int)peer_port, data_buffer);
    
                    nx_packet_release(RecPacket);
                    
                    /* 申请发送数据包 */
                    ret = nx_packet_allocate(&pool_0, &TraPacket, NX_TCP_PACKET, TX_WAIT_FOREVER);
    
                    if (ret)
                    {
                        Error_Handler(__FILE__, __LINE__);  
                    }
    
                    sprintf((char *)sendbuf, "sendbuf = %d\r\n", count++);
                    
                    /*将要发送的数据附加到TraPacket */
                    ret = nx_packet_data_append(TraPacket, (VOID *)sendbuf, strlen((char *)sendbuf), 
    &pool_0, TX_WAIT_FOREVER);
    
                    if (ret)
                    {
                        Error_Handler(__FILE__, __LINE__);
                    }
        
                    /* 发送数据,注意发送后,此函数会释放数据包 */
                    ret =  nx_tcp_socket_send(&TCPSocket,   
                                               TraPacket,       
                                                NX_WAIT_FOREVER); 
                    
                    
                    if (ret)
                    {
                        //Error_Handler(__FILE__, __LINE__);
                    }
                    
                }
                else
                {
                    /* 释放数据包 */
                    nx_packet_release(RecPacket);
                    
                    /* 断开连接 */
                    nx_tcp_socket_disconnect(&TCPSocket, 
                                             NX_WAIT_FOREVER);
                    
                    /* disconnect the socket */
                    nx_tcp_client_socket_unbind(&TCPSocket);
                }
            }
        }      
    
         /* 省略 */
    }

    9.4   网络调试助手和板子的调试操作步骤

    我们这里使用下面这款调试助手,当然,任何其它网络调试助手均可,不限制:

     http://www.armbbs.cn/forum.php?mod=viewthread&tid=1568

    9.4.1      测试使用的DM916X网口并注意跳线帽

    测试时,网线要插到DM916X网口上:

     

    特别注意此处跳线帽的位置,要短接PG11:

     

    9.4.2      RJ45网络变压器插座上绿灯和黄灯现象

    各种网卡、交换机等网络设备都不一样,一般来讲:绿灯分为亮或不亮(代表网络速度),黄灯分为闪烁或不闪烁(代表是否有数据收发)。

    绿灯:长亮代表100M; 不亮代表10M。

    黄灯:长亮代表无数据收发; 闪烁代表有数据收发。

    也有些千兆网卡的灯以颜色区分,不亮代表10M / 绿色代表100M / 黄色代表1000M。现在10M的网络基本看不到了,如果一个灯长亮,基本可以说明100M网络或更高,而另一个灯时而闪烁,那代表有数据收发,具体要看网络设备了。甚至有些低等网卡如TP-LINK,只有一个灯,亮代表连通,闪烁代表数据收发。

    对于开发板上面的RJ45网络变压器插座上面的灯而言,绿灯代表数据收发,长亮的话表示无数据收发,闪烁代表有数据收发。黄灯代表网络速度,长亮代表100M,不亮代表10M。

    9.4.3      第1步,设置板子IP地址

    我们这里使用使用固定IP(或者说静态IP一个意思),设置也比较省事。我们这里以开发板和电脑直连的方式进行说明,即通过一根网线直接将开发板的网口和电脑端的网口连接起来即可。如果大家使用的是笔记本,强烈推荐测试期间将笔记本的WIFI网络禁止,各种代理软件和虚拟网卡也暂时关闭。等测试完毕了再逐一打开,查看是否有问题。

    对于固定IP方式,也可以接到路由器或者交换机上面测试,特别注意板子设置的IP地址不要跟路由器或者交换机上其它设备的IP冲突了,测试阶段还是建议采用电脑直连方式,跑通了再跑其它方式。

    在文件demo_dm9162_netx.h中设置IP地址,具体配置如下(大家更新自己的情况修改):

    /*
    *********************************************************************************************************
    *                                        IP相关
    *********************************************************************************************************
    */
    #define DEFAULT_PORT                    1000    /* TCP服务器监听端口号 */
    
    #define IP_ADDR0                        192
    #define IP_ADDR1                        168
    #define IP_ADDR2                        28
    #define IP_ADDR3                        245       

    9.4.4      第2步,设置电脑IP地址

    一定要将电脑端的IP地址设置到跟开发板在一个IP段,即都是192.168.28.X。第2步中已经将开发板的IP设置为192.168.28.245,我们这里就将电脑的IP设置为192.168.28.221。我这里是WIN7 64bit系统。

    (1)右击桌面上的“网络”图标,选择属性。

    (2)弹出的界面中选项“本地连接”

    (3)选择“属性(P)”

    (4)双击“Internet协议版本4(TCP/Ipv4)”选项。

    (5)配置IP地址、子网掩码和默认网关,DNS无需配置,记得点击确定

    (6)点击了“确定”按钮后,退回到之前的界面,这里的“确定”按钮不要忘了点击:

    9.4.5      第3步,测试ping是否成功

    下载例程到开发板,然后ping 192.168.28.245,查看是否连接上。

    (1)WIN+R组合键打开“运行”窗口,输入cmd。

    (2)输入ping 192.168.28.245后,回车,也是可以的。

    收发相同,没有数据丢失,说明ping命令也是成功的。

    9.4.6  第4步,在程序中配置要访问的远程IP地址和端口

    根据第2步设置的电脑端IP地址,需要大家配置程序中app_tcpnet_lib.c文件开头的宏定义,其中IP地址填前面获取的192.168.28.146,大家要根据电脑实际的IP地址填写。而端口号,我们这里随意配置一个即可,配置为1001,后面电脑端使用网络调试助手创建TCP服务器时,务必要跟这个端口号统一:

    /* 远程服务器端口和IP */
    #define TCP_SERVER_PORT                 1001
    #define TCP_SERVER_ADDRESS              IP_ADDRESS(192, 168, 28, 146)

    9.4.7      第5步,网络调试助手创建TCP服务器

    •   打开调试助手,点击左上角创建服务器:

    •   弹出如下界面,指定IP设置为192.168.28.146,一定要跟第2步设置的电脑IP地址一致,端口号1001,最后点击确定:

    •   创建后的界面效果如下:

    •   点击启动服务器:

    9.4.8  第6步,创建TCP客户端连接TCP服务器

    如果开发板下载了TCP客户端的程序,并且开发板已经上电,可以看到客户端连接已经加入:

    跟我们在程序中设置的端口号,即app_tcpnet_lib.c文件开头的宏定义:

    #define DEFAULT_PORT    1000是一致的。

    9.4.9      第5步,TCP客户端回环测试

    板子和网络调试助手建立连接后就可以相互收发数据了。

    板子端接收到字符做了个简单的展示(波特率115200,数据位8,奇偶校验位无,停止位1):

     

    9.5   实验例程

    配套例子:

    V5-2403_ThreadX NetXDUO TCP Client

    实验目的:

    1. 学习ThreadX NetXDUO TCP 客户端实现

    实验内容:

    1. 共创建了如下几个任务,通过按下按键K1可以通过串口打印任务堆栈使用情况                                   

              ======================================================

                         OS CPU Usage =  1.31%          

            =======================================================

              任务优先级 任务栈大小 当前使用栈  最大栈使用   任务名

               Prio     StackSize   CurStack    MaxStack   Taskname

               2         4092        303         459      App Task Start

               30         1020        303         303      App Task STAT

               31         1020        87          71      App Task IDLE

               5          4092        311         551      App Msp Pro

               7         4092        303         719      App Task UserIF

               6         4092        255         359      App NETX Pro

               3         4092        415         535      NetX IP Instance 0

               0         1020        191         235      System Timer Thread   

    串口软件可以使用SecureCRT或者H7-TOOL RTT查看打印信息。

    App Task Start任务  :启动任务,这里用作BSP驱动包处理。

    App Task MspPro任务 :消息处理。

    App Task UserIF任务 :按键消息处理。

    App Task COM任务   :这里用作LED闪烁。

    System Timer Thread任务:系统定时器任务

    操作说明:

    1、由于程序使用了DWT时钟周期计数器,程序下载后,请将板子重新上电使用,防止DWT时钟周期计数器没有正常复位。

    2、NetX网络协议栈操作:

    (1) 默认IP地址192.168.28.245,在demo_dm9162_netx.c开头定义,用户可根据需要修改。

    (2) 可以在电脑端用网络调试软件创建TCP服务器,端口号1001。

    (3) 实现了一个简单的回环通信,用户使用上位机发送的数据,然后板子返回另外的数据。

    串口打印信息方式(AC5,AC6和IAR):

    波特率 115200,数据位 8,奇偶校验位无,停止位 1

     

    9.6   总结

    本章节就为大家讲解这么多,希望大家多做测试,争取可以熟练掌握这些API函数的使用。

    微信公众号:armfly_com 安富莱论坛:www.armbbs.cn 安富莱淘宝:https://armfly.taobao.com
  • 相关阅读:
    Android框架之路——OkGo的使用
    recyclerview23+出现多个item只显示第一个item的问题
    Spark MLlib回归算法LinearRegression
    Spark MLlib聚类KMeans
    Impala性能优化
    Impala通过JDBC方式访问
    Impala与HBase整合
    Impala数据处理(加载和存储)
    Impala SQL
    Impala储存与分区
  • 原文地址:https://www.cnblogs.com/armfly/p/15792976.html
Copyright © 2011-2022 走看看