ThreadX NetXDUO之TCP服务器

Posted Simon223

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ThreadX NetXDUO之TCP服务器相关的知识,希望对你有一定的参考价值。

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

第8章   ThreadX NetXDUO之TCP服务器

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

目录

8.1   初学者重要提示

8.2   TCP服务器API函数

8.2.1  函数nx_system_initialize

8.2.2  函数nx_packet_pool_create

8.2.3  函数nx_ip_create

8.2.4  函数nx_arp_enable

8.2.5  函数nx_ip_fragment_enable

8.2.6  函数nx_tcp_enable

8.2.7  函数nx_tcp_socket_create

8.2.8  函数nx_tcp_server_socket_listen

8.2.9  函数nx_tcp_server_socket_relisten

8.2.10        函数nx_tcp_server_socket_accept

8.2.11        函数nx_tcp_server_socket_unaccept

8.2.12        函数nx_tcp_socket_info_get

8.2.13        函数nx_tcp_socket_receive

8.2.14        函数nx_tcp_socket_send

8.2.15        函数nx_packet_data_retrieve

8.2.16        函数nx_packet_release

8.2.17        函数nx_tcp_socket_disconnect

8.3   TCP服务器的实现方法

8.3.1  NetXDUO初始化

8.3.2  TCP服务器实现

8.3.3  TCP回环通信实现

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

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

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

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

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

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

8.4.6      第3步,网络调试助手创建TCP客户端

8.4.7      第5步,TCP服务器回环测试

8.5   实验例程

8.6   总结


8.1   初学者重要提示

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

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

3、  socket和监听的关系:

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

8.2   TCP服务器API函数

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

 

8.2.1  函数nx_system_initialize

函数原型:

VOID nx_system_initialize(VOID); 

函数描述:

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

8.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);                        /* 内存池大小 */  

8.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任务优先级 */

8.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 )


8.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);

8.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);

8.2.7  函数nx_tcp_socket_create

函数原型:

UINT nx_tcp_socket_create(
    NX_IP *ip_ptr, 
    NX_TCP_SOCKET *socket_ptr,
    CHAR *name, 
    ULONG type_of_service, 
    ULONG fragment,
    UINT time_to_live, 
    ULONG window_size,
    VOID (*urgent_data_callback)(NX_TCP_SOCKET *socket_ptr),
    VOID (*disconnect_callback)(NX_TCP_SOCKET *socket_ptr));

函数描述:

此函数用于创建TCP Socket。

函数参数:

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

2、  第2个参数是TCP控制块地址。

3、  第3个参数是任务控制块名字。

4、  第4个参数是服务类型,支持的参数如下:

  •   NX_IP_NORMAL (0x00000000)
  •   NX_IP_MIN_DELAY (0x00100000)
  •   NX_IP_MAX_DATA (0x00080000)
  •   NX_IP_MAX_RELIABLE (0x00040000)
  •   NX_IP_MIN_COST (0x00020000)

5、  第5个参数指定是否允许进行 IP 分段。 如果指定 NX_FRAGMENT_OKAY (0x0),则允许进行 IP 分段。 如果指定 NX_DONT_FRAGMENT (0x4000),则会禁止进行 IP 分段。

6、  第6个参数是指定一个 8 位的值,用于定义此数据包在被丢弃之前可通过的路由器数目。 默认值由 NX_IP_TIME_TO_LIVE 指定。

7、  第7个参数定义TCP Socket接收队列中允许的最大字节数。

8、  第8个参数用于在接收流中检测到紧急数据时调用的回调函数。如果此值为 NX_NULL,则会忽略紧急数据。

9、  第9个参数是TCP Socket另一端发出断开连接时调用的回调函数。如果此值为 NX_NULL,则会禁用断开连接回调函数。

10、  返回值:

  •  NX_SUCCESS:(0x00) 创建 TCP Socket成功。
  •  NX_OPTION_ERROR:(0x0A) 服务类型、分段、窗口大小或生存时间选项无效。
  •  NX_PTR_ERROR:(0x07) IP 实例或TCP Socket指针无效。
  •  NX_CALLER_ERROR:(0x11) 此服务的调用方无效。
  •  NX_NOT_ENABLED:(0x14) 此组件尚未启用。

注意事项:

  1. 应用程序回调(第9个参数)是在IP任务里面调用的。

使用举例:

/* 创建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另一端发出断开连接时调用的回调函数 */

8.2.8  函数nx_tcp_server_socket_listen

函数原型:

UINT nx_tcp_server_socket_listen(
     NX_IP *ip_ptr, UINT port,
     NX_TCP_SOCKET *socket_ptr,
     UINT listen_queue_size,
     VOID (*listen_callback)(NX_TCP_SOCKET *socket_ptr, UINT port));

函数描述:

此函数用于监听指定 TCP 端口上的客户端连接请求。 接收到客户端连接请求时,提供的服务器Socket就会与指定的端口绑定,并调用所提供的监听回调函数。

如果应用程序希望在同一端口上处理其他客户端连接,则必须使用可用的Socket(处于关闭状态的Socket)调用 nx_tcp_server_socket_relisten来建立下一个连接。 在调用重新监听之前,其他客户端连接会进行排队。 超过最大队列深度时,就会丢弃最早的连接请求,以此将新连接请求排入队列。 最大队列深度由此函数指定。

函数参数:

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

2、  第2个参数是要监听的端口号,范围0 – 0xFFFF。

3、  第3个参数是Socket地址。

4、  第4个参数是可以监听的连接数。

5、  第5个参数是监听回调函数,如果设置为NULL,则不使用监听回调。

6、  返回值

  •   NX_SUCCESS:(0x00) 启用 TCP 端口监听成功。
  •   NX_MAX_LISTEN:(0x33) 没有更多的监听请求供使用,nx_api.h 中定义的常量NX_MAX_LISTEN_REQUESTS 定义了监听请求数上限。
  •   NX_NOT_CLOSED:(0x35) 提供的Socket未处于关闭状态。
  •   NX_ALREADY_BOUND:(0x22) 提供的Socket已与某个端口绑定。
  •   NX_DUPLICATE_LISTEN:(0x34) 此端口已有Socket请求。
  •   NX_INVALID_PORT:(0x46) 指定了无效的端口。
  •   NX_PTR_ERROR:(0x07) IP实例地址 或Socket地址无效。
  •   NX_CALLER_ERROR:(0x11) 此服务的调用方无效。
  •   NX_NOT_ENABLED:(0x14) 此组件尚未启用。

注意事项:

  1. 监听回调是在IP任务里面调用的。

使用举例:

/*
    * 监听新的链接。
    * 创建回调TCP_listen_callback表示监听到新连接。
    */
    ret = nx_tcp_server_socket_listen(&ip_0,                  /* IP实例控制块 */  
                                  DEFAULT_PORT,           /* 默认端口 */          
                                      &TCPSocket,             /* TCP Socket控制块 */
                                      MAX_TCP_CLIENTS,        /* 可以监听的连接数 */
                                      NULL);                  /* 监听回调函数 */

8.2.9  函数nx_tcp_server_socket_relisten

函数原型:

UINT nx_tcp_server_socket_relisten(
    NX_IP *ip_ptr, 
    UINT port,
    NX_TCP_SOCKET *socket_ptr);

函数描述:

前面监听的端口上接收到连接之后,可以调用次函数。此函数的主要目的是提供新的Socket用于下一个客户端连接。如果已有排队中的连接请求,则调用此函数期间就会立即处理该连接。

函数参数:

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

2、  第2个参数是监听的端口号,范围0 – 0xFFFF。

3、  第3个参数是用于下一客户端连接的Socket。

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

  •   NX_SUCCESS:(0x00) 重新监听TCP 端口成功。
  •   NX_NOT_CLOSED:(0x35) 提供的Socket未处于关闭状态。
  •   NX_ALREADY_BOUND:(0x22) 提供的Socket已与某个端口绑定。
  •   NX_INVALID_RELISTEN:(0x47) 此端口已有一个有效的Socket或者指定的端口监听请求。
  •   NX_CONNECTION_PENDING:(0x48) 与 NX_SUCCESS 相同,只不过存在已排队的连接请求,并且已在调用期间处理该请求。
  •   NX_INVALID_PORT:(0x46) 指定了无效的端口。
  •   NX_PTR_ERROR:(0x07) IP 实例地址或监听回调无效。
  •   NX_CALLER_ERROR:(0x11) 此服务的调用方无效。
  •   NX_NOT_ENABLED:(0x14) 此组件尚未启用。

注意事项:

  1. 函数nx_tcp_server_socket_listen设置的监听回调,此函数也会调用。

使用举例:

  /* 重新监听 */
   nx_tcp_server_socket_relisten(&ip_0, 
                              DEFAULT_PORT, 
                             &TCPSocket);

8.2.10        函数nx_tcp_server_socket_accept

函数原型:

UINT nx_tcp_server_socket_accept(
         NX_TCP_SOCKET *socket_ptr,
         ULONG wait_option);    

函数描述:

此函数用于接收TCP客户端连接请求。在用户调用了监听或者重新监听函数后,又或者监听回调函数之后,可以立即调用此函数。无法立即建立连接的时候,此函数会根据等待参数挂起。

函数参数:

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

2、  第2个参数是等待选项,支持的参数如下:

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

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

  •   NX_SUCCESS:(0x00) 接受 TCP Socket(被动连接)成功。
  •   NX_NOT_LISTEN_STATE:(0x36) 提供的TCP 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) 此组件尚未启用。

注意事项:

  1. 不再需要该连接之后,应用程序必须调用 nx_tcp_server_socket_unaccept,以删除Socket与服务器端口的绑定。
  2. 应用程序回调是在 IP任务中调用的。

使用举例:

/* 启动TCP通信前,接收新连接 */
ret = nx_tcp_server_socket_accept(&TCPSocket,       /* TCP Socket控制块 */
                               TX_WAIT_FOREVER); /* 等待连接 */

if (ret)

    Error_Handler(__FILE__, __LINE__);

8.2.11        函数nx_tcp_server_socket_unaccept

函数原型:

UINT nx_tcp_server_socket_unaccept(NX_TCP_SOCKET *socket_ptr);

函数描述:

此服务用于删除Socket与服务器端口的绑定。在断开连接之后,或者没有成功的接收连接时,应用程序必须调用此函数。

函数参数:

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

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

  •   NX_SUCCESS:(0x00) 取消接受服务器套接字成功。
  •   NX_NOT_LISTEN_STATE:(0x36) 服务器套接字处于不正确的状态,可能未断开连接。
  •   NX_PTR_ERROR:(0x07) Socket指针无效。
  •   NX_CALLER_ERROR:(0x11) 此服务的调用方无效。
  •   NX_NOT_ENABLED:(0x14) 此组件尚未启用。

使用举例:

/* 解除Socket和服务器端口的绑定 */
nx_tcp_server_socket_unaccept(&TCPSocket);

8.2.12        函数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);          /* 当前接收窗口大小 */

8.2.13        函数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);  /* 永久等待 */

8.2.14        函数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); /* 永久等待 */

8.2.15        函数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);   /* 数据大小 */

8.2.16        函数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) 追加指针大于有效负载结束位置。

注意事项:

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

8.2.17        函数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);

8.3   TCP服务器的实现方法

8.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);

    /* 省略 */

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

8.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__);
	

     /* 省略 */

8.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)
        
			/* 启动TCP通信前,接收新连接 */
			ret = nx_tcp_server_socket_accept(&TCPSocket,       /* TCP Socket控制块 */
			                                  TX_WAIT_FOREVER); /* 等待连接 */

			if (ret)
			
				Error_Handler(__FILE__, __LINE__);
			
        
        
        if((socket_state == NX_TCP_ESTABLISHED)&&(ret == NX_SUCCESS))
        
                
            /* 接收TCP客户端发的TCP数据包 */
            ret = nx_tcp_socket_receive(&TCPSocket,        /* TCP Socket控制块 */
			                            &data_packet,      /* 接收到的数据包 */
			                            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(data_packet,    /* 接收到的数据包 */
				                        data_buffer,    /* 解析出数据 */
				                        &bytes_read);   /* 数据大小 */

                /* 打印接收到数据 */
                PRINT_DATA(peer_ip_address, (unsigned int)peer_port, data_buffer);

                /* 立即将接收到的数据发送回去 */
                ret =  nx_tcp_socket_send(&TCPSocket,       /* TCP Socket控制块 */
				                          data_packet,      /* 数据包 */
				                          NX_WAIT_FOREVER); /* 永久等待 */
            
            else
            
                /* 断开连接 */
                nx_tcp_socket_disconnect(&TCPSocket, 
				                         NX_WAIT_FOREVER);
				
			 /* 解除Socket和服务器端口的绑定 */
                nx_tcp_server_socket_unaccept(&TCPSocket);
				
			  /* 重新监听 */
                nx_tcp_server_socket_relisten(&ip_0, 
				                              DEFAULT_PORT, 
				                              &TCPSocket);
            
        
	

     /* 省略 */

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

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

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

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

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

 

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

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

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

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

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

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

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

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

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

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

/*
*********************************************************************************************************
*                                        IP相关
*********************************************************************************************************
*/
#define DEFAULT_PORT                    1001    /* TCP服务器监听端口号 */

#define IP_ADDR0                        192
#define IP_ADDR1                        168
#define IP_ADDR2                        28
#define IP_ADDR3                        245    

8.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)点击了“确定”按钮后,退回到之前的界面,这里的“确定”按钮不要忘了点击:

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

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

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

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

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

8.4.6      第3步,网络调试助手创建TCP客户端

  •   打开调试助手,点击左上角创建连接:

 

  •   弹出如下界面,类型选择TCP,目标IP设置为192.168.28.245端口号1001,最后点击创建:

 

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

 

  •   点击连接,连接后的界面效果如下:

 

8.4.7      第5步,TCP服务器回环测试

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

 发送和接收一致,说明移植是没问题的。

8.5   实验例程

配套例子:

V6-2402_ThreadX NetXDUO TCP Server

实验目的:

  1. 学习ThreadX NetXDUO TCP Server实现

实验内容:

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

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

       OS CPU Usage =  1.31%          

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

以上是关于ThreadX NetXDUO之TCP服务器的主要内容,如果未能解决你的问题,请参考以下文章

ThreadX NetXDUO之TCP服务器

STM32H7第8章 ThreadX NetXDUO之TCP服务器

ThreadX NetXDUO之TCP客户端

ThreadX NetXDUO之TCP客户端

ThreadX NetXDUO之TCP客户端

ThreadX NetXDUO之UDP客户端/服务器