其他分享
首页 > 其他分享> > 【STM32F407】第8章 ThreadX NetXDUO之TCP服务器

【STM32F407】第8章 ThreadX NetXDUO之TCP服务器

作者:互联网

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

第8章   ThreadX NetXDUO之TCP服务器

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

8.1 初学者重要提示

8.2 TCP服务器API函数

8.3 TCP服务器的实现方法

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

8.5 实验例程说明

8.6 总结

 

 

8.1   初学者重要提示

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

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

3、  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、  返回值:

使用举例:

  /* 创建内存池 */
    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、  返回值

使用举例:

/* 例化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、  返回值

使用举例:

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、  返回值

使用举例:

    /* 使能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、  返回值

使用举例:

    /* 使能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个参数是服务类型,支持的参数如下:

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、  返回值:

注意事项:

  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、  返回值

注意事项:

  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、  返回值,返回以下几种状态值:

注意事项:

  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个参数是等待选项,支持的参数如下:

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

注意事项:

  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、  返回值,返回以下几种状态值:

使用举例:

/* 解除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、  返回值:

使用举例:

   /* 获取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队列上没有数据时的处理:

4、  返回值:

注意事项:

  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个参数是发送的数据包大于接收方窗口大小时的参数处理,支持如下参数:

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

注意事项:

  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_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、返回值,返回以下几种状态值:

注意事项:

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

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个参数等待断开连接时,支持的参数:

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

使用举例:

/* 断开连接 */
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客户端

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%          

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

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

           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 Client连接此服务器端,端口号1001。

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

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

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

 

8.6   总结

本章节主要为大家讲解了TCP服务器的实现,大家也可以创建各种玩法加强认识。

 

标签:nx,Socket,IP,NetXDUO,NX,TCP,STM32F407,数据包
来源: https://www.cnblogs.com/armfly/p/15775648.html