admin管理员组

文章数量:1530519

最新教程下载:ThreadX NetXDUO网络协议栈教程更新记录贴,前11章已经发布(2022-01-03) - uCOS & uCGUI & emWin & embOS & TouchGFX & ThreadX - 硬汉嵌入式论坛 - Powered by Discuz!

第9章   ThreadX NetXDUO之TCP客户端

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

目录

9.1   初学者重要提示

9.2   TCP客户端API函数

9.2.1  函数nx_system_initialize

9.2.2  函数nx_packet_pool_create

9.2.3  函数nx_ip_create

9.2.4  函数nx_arp_enable

9.2.5  函数nx_ip_fragment_enable

9.2.6  函数nx_tcp_enable

9.2.7  函数nx_tcp_client_socket_bind

9.2.8  函数nx_tcp_client_socket_unbind

9.2.9  函数nx_tcp_client_socket_connect

9.2.10        函数nx_tcp_socket_info_get

9.2.11        函数nx_tcp_socket_receive

9.2.12        函数nx_tcp_socket_send

9.2.13        函数nx_packet_data_retrieve

9.2.14        函数nx_packet_release

9.2.15        函数nx_tcp_socket_disconnect

9.3   TCP客户端的实现方法

9.3.1  NetXDUO初始化

9.3.2  TCP客户端实现

9.3.3  TCP回环通信实现

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

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

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

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

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

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

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

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

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

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

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/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函数的使用。

本文标签: 客户端ThreadXTCPNetXDUO