【RL-TCPnet网络教程】第15章 RL-TCPnet之创建多个TCP连接

第15章     RL-TCPnet之创建多个TCP连接

本章节为大家讲解RL-TCPnet的TCP多客户端实现,因为多客户端在实际项目中用到的地方还挺多,所以我们也专门开启一个章节做讲解。另外,学习本章节前,务必要优先学习第14章TCP客户端。学会创建一个TCP客户端了,创建多个客户端是一样的。

本章教程含STM32F407开发板和STM32F429开发板。

15.1  初学者重要提示

15.2  创建多个TCP客户端连接服务器

15.3  TCP配置说明(Net_Config.c)

15.4  TCP调试说明(Net_Debug.c)

15.5  TCP客户端的实现方法

15.6  网络调试助手创建TCP服务器的方法

15.7  实验例程说明(裸机)

15.8  实验例程说明(RTX)

15.9  总结

 

15.1  初学者重要提示

1、学习本章节前,务必保证已经学习了第12章的TCP基础知识和第14章的TCP客户端。

2、相比前面章节的TCP服务器,TCP客户端的测试要稍麻烦些,例子中默认访问的TCP服务器端IP地址是192.168.1.2,端口号1001。大家测试时要根据自己电脑的实际IP地址设置app_tcpnet_lib.c文件中远程IP和端口。具体测试方法详看本章节的15.6小节。

15.2  创建多个TCP客户端连接服务器

本章节为大家讲解一个多TCP客户端连接服务器的实例,因为实际项目中,这种情况还比较多,所以也作为一期教程进行专门的讲解。

有了上期教程的基础,本期教程也比较好实现,用户仅需多创建几个TCP客户端,并配置Net_Config.c中可以创建的TCP Socket个数即可。本章节配套的例子是创建了三个TCP客户端跟电脑端的服务器连接通信。

15.3  TCP配置说明(Net_Config.c)

(本章节配套例子的配置与本小节的说明相同)

RL-TCPnet的配置工作是通过配置文件Net_Config.c实现。在MDK工程中打开文件Net_Config.c,可以看到下图所示的工程配置向导:

【RL-TCPnet网络教程】第15章 RL-TCPnet之创建多个TCP连接

RL-TCPnet要配置的选项非常多,我们这里把几个主要的配置选项简单介绍下。

【RL-TCPnet网络教程】第15章 RL-TCPnet之创建多个TCP连接

System Definitions

(1)Local Host Name

局域网域名。

这里起名为armfly,使用局域网域名限制为15个字符。

(2)Memory Pool size

参数范围1536-262144字节。

内存池大小配置,单位字节。另外注意一点,配置向导这里显示的单位是字节,如果看原始定义,MDK会做一个自动的4字节倍数转换,比如我们这里配置的是8192字节,那么原始定义是#define MEM_SIZE  2048,也就是8192/4 = 2048。

(3)Tick Timer interval

可取10,20,25,40,50,100,200,单位ms。

系统滴答时钟间隔,也就是网络协议栈的系统时间基准,默认情况下,取值100ms。

【RL-TCPnet网络教程】第15章 RL-TCPnet之创建多个TCP连接

Ethernet Network Interface

以太网接口配置,勾选了此选项就可以配置了,如果没有使能DHCP的话,将使用这里配置的固定IP

(1)MAC Address

局域网内可以随意配置,只要不跟局域网内其它设备的MAC地址冲突即可。

(2)IP Address

IP地址。

(3)Subnet mask

子网掩码。

(4)Default Gateway

默认网关。

【RL-TCPnet网络教程】第15章 RL-TCPnet之创建多个TCP连接

Ethernet Network Interface

以太网接口配置,这个配置里面还有如下两项比较重要的配需要说明。

(1)NetBIOS Name Service

NetBIOS局域网域名服务,这里打上对勾就使能了。这样我们就可以通过前面配置的Local Host Name局域网域名进行访问,而不需要通过IP地址访问了。

(2)Dynaminc Host Configuration

即DHCP,这里打上对勾就使能了。使能了DHCP后,RL-TCPnet就可以从外接的路由器上获得动态IP地址。

【RL-TCPnet网络教程】第15章 RL-TCPnet之创建多个TCP连接

UDP Sockets

UDP Sockets配置,打上对勾就使能了此项功能

(1)Number of UDP Sockets

用于配置可创建的UDP Sockets数量。

范围1 – 20。

【RL-TCPnet网络教程】第15章 RL-TCPnet之创建多个TCP连接

TCP Sockets

TCP Sockets配置,打上对勾就使能了此项功能

(1)Number of TCP Sockets

用于配置可创建的TCP Sockets数量,由于要创建三个TCP客户端,这里配置为4,可以创建4个Socket。

(2)Number of Retries

范围0-20。

用于配置重试次数,TCP数据传输时,如果在设置的重试时间内得不到应答,算一次重试失败,这里就是配置的最大重试次数。

(3)Retry Timeout in seconds

范围1-10,单位秒。

重试时间。如果发送的数据在重试时间内得不到应答,将重新发送数据。

(4)Default Connect Timeout in seconds

范围1-600,单位秒。

用于配置默认的保持连接时间,即我们常说的Keep Alive时间,如果时间到了将断开连接。常用于HTTP Server,Telnet Server等。

(5)Maximum Segment Size

范围536-1460,单位字节。

MSS定义了TCP数据包能够传输的最大数据分段。

(6)Receive Window Size

范围536-65535,单位字节。

TCP接收窗口大小。

15.4 TCP调试说明(Net_Debug.c)

(重要说明,RL-TCPnet的调试是通过串口打印出来的)

RL-TCPnet的调试功能是通过配置文件Net_Debug.c实现。在MDK工程中打开文件Net_Debug.c,可以看到下图所示的工程配置向导:

【RL-TCPnet网络教程】第15章 RL-TCPnet之创建多个TCP连接

Print Time Stamp

勾选了此选项的话,打印消息时,前面会附带时间信息。

其它所有的选项

默认情况下,所有的调试选项都关闭了,每个选项有三个调试级别可选择,这里我们以Memory Management Debug为例,点击下拉列表,可以看到里面有Off,Errors only和Full debug三个调试级别可供选择,每个调试选项里面都是这三个级别。

【RL-TCPnet网络教程】第15章 RL-TCPnet之创建多个TCP连接

Off:表示关闭此选项的调试功能。

Errors only:表示仅在此选项出错时,将其错误打印出来。

Full debug:表示此选项的全功能调试。

具体测试,我们这里就不做了,大家可以按照第11章讲解的调试方法进行测试。

15.5 TCP客户端的实现方法

有了本章节15.3小节的配置后,剩下的问题就是TCP客户端的创建和TCP客户端数据收发的实现。

15.5.1 创建三个TCP客户端

TCP服务器的创建比较简单,调用函数tcp_get_socket即可(此函数的使用方法和注意事项在上一章的14.2.1小节有讲解),为了更好的管理这三个TCP客户端,专门为每个TCP客户端单独做一个C文件:

【RL-TCPnet网络教程】第15章 RL-TCPnet之创建多个TCP连接

app_tcpclient1.c,app_tcpclient2.c和app_tcpclient3.c分别是TCP客户端1,2和3。下面以TCP客户端1为例说明,即app_tcpclient1.c文件:

/*

*********************************************************************************************************

*                                         变量

*********************************************************************************************************

*/

uint8_t socket_tcp1;

/*

*********************************************************************************************************

*    函 数 名: tcp_callback

*    功能说明: TCP Socket的回调函数

*    形    参: soc  TCP Socket类型

*             evt  事件类型

*             ptr  事件类型是TCP_EVT_DATA,ptr指向的缓冲区记录着接收到的TCP数据,其余事件记录IP地址

*             par  事件类型是TCP_EVT_DATA,记录接收到的数据个数,其余事件记录端口号

*    返 回 值: 主要是TCP_EVT_CONREQ事件使用,返回数值1允许连接,返回数值0禁止连接。

*********************************************************************************************************

*/

static U16 tcp_callback1 (U8 soc, U8 evt, U8 *ptr, U16 par)

{

     char buf[];

     uint16_t i;

     /* 确保是socket_tcp的回调 */

     if (soc != socket_tcp1)

     {

         return ();

     }

     switch (evt)

     {

         /*

              远程客户端连接消息

             1、数组ptr存储远程设备的IP地址,par中存储端口号。

             2、返回数值1允许连接,返回数值0禁止连接。

         */

         case TCP_EVT_CONREQ:

              sprintf(buf, "远程客户端请求连接IP: %d.%d.%d.%d", ptr[], ptr[], ptr[], ptr[]);

              printf_debug1("IP:%s  port:%d\r\n", buf, par);

              return ();

         /* 连接终止 */

         case TCP_EVT_ABORT:

              break;

         /* Socket远程连接已经建立 */

         case TCP_EVT_CONNECT:

              printf_debug1("Socket is connected to remote peer\r\n");

              break;

         /* 连接断开 */

         case TCP_EVT_CLOSE:

              printf_debug1("Connection has been closed\r\n");

              break;

         /* 发送的数据收到远程设备应答 */

         case TCP_EVT_ACK:

              break;

         /* 接收到TCP数据帧,ptr指向数据地址,par记录数据长度,单位字节 */

         case TCP_EVT_DATA:

              printf_debug1("TCP客户端1接收到数据长度 = %d\r\n", par);

              for(i = ; i < par; i++)

              {

                   printf_debug1("ptr[%d] = %d\r\n", i, ptr[i]);

              }

              break;

     }

     return ();

}

/*

*********************************************************************************************************

*    函 数 名: TcpConnect_Client1

*    功能说明: 创建一个TCP Client

*    形    参: _remip     要访问的远程IP地址

*             _remport   远程端口号

*             _localport 本地端口号

*    返 回 值: 无

*********************************************************************************************************

*/

void TcpConnect_Client1(uint8_t *_remip, uint16_t _remport, uint16_t _localport)

{

     uint8_t res;

     /*

        创建TCP Socket并连接,客户端连接服务器后,10秒内无数据通信将断开连接。

        但是由于这里使能了TCP_TYPE_KEEP_ALIVE,会一直保持连接,不受10秒的时间限制。

     */

    socket_tcp1 = tcp_get_socket (TCP_TYPE_CLIENT | TCP_TYPE_KEEP_ALIVE, , , tcp_callback1);

     if(socket_tcp1 != )

     {

         res = tcp_connect (socket_tcp1, _remip, _remport, _localport);

         printf_debug1("TCP Socket创建成功res = %d\r\n", res);

     }

}

在这个文件中,专门对TCP客户端的创建进行了封装,这样仅需调用函数TcpConnect_Client1就可以创建TCP客户端了。此函数是在app_tcpnet_lib.c文件中被调用的,通过按下按键K1进行调用:

/*

*********************************************************************************************************

*                                      外部变量和函数调用

*********************************************************************************************************

*/

extern uint8_t socket_tcp1;

extern void TcpConnect_Client1(uint8_t *_remip, uint16_t _remport, uint16_t _localport);

extern uint8_t socket_tcp2;

extern void TcpConnect_Client2(uint8_t *_remip, uint16_t _remport, uint16_t _localport);

extern uint8_t socket_tcp3;

extern void TcpConnect_Client3(uint8_t *_remip, uint16_t _remport, uint16_t _localport);

/*

*********************************************************************************************************

*                              宏定义,远程服务器的IP和端口

*********************************************************************************************************

*/

/* 要访问的远程服务器IP和端口配置,也就是电脑端调试助手设置的IP和端口号 */

#define IP1            192

#define IP2            168

#define IP3            1

#define IP4            2

#define PORT_NUM         1001

/* 这个是本地端口 */

#define LocalPort_NUM1    1024

#define LocalPort_NUM2    1025

#define LocalPort_NUM3    1026

/*

*********************************************************************************************************

*                                         变量

*********************************************************************************************************

*/

uint8_t Rem_IP[] = {IP1,IP2,IP3,IP4};

/* TCP状态的返回值 */

const char * ReVal_Table[]=

{

     "0: TCP_STATE_FREE",                                        

     "1: TCP_STATE_CLOSED",

     "2: TCP_STATE_LISTEN",

     "3: TCP_STATE_SYN_REC",

     "4: TCP_STATE_SYN_SENT",                                         

     "5: TCP_STATE_FINW1",

     "6: TCP_STATE_FINW2",

     "7: TCP_STATE_CLOSING",

     "8: TCP_STATE_LAST_ACK",                                          

     "9: TCP_STATE_TWAIT",

     "10: TCP_STATE_CONNECT",

     "11: 连接还未创建",

};

/*

*********************************************************************************************************

*    函 数 名: tcpnet_poll

*    功能说明: 使用TCPnet必须要一直调用的函数

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

void tcpnet_poll(void)

{

     if(bsp_CheckTimer())

     {

         bsp_LedToggle();

         /* 此函数坚决不可以放在中断里面跑 */

         timer_tick ();

     }

     main_TcpNet ();

}

/*

*********************************************************************************************************

*    函 数 名: TCPnetTest

*    功能说明: TCPnet应用

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

void TCPnetTest(void)

{ 

     int32_t iCount;

     uint8_t *sendbuf;

     uint16_t maxlen;

     uint8_t ucKeyCode;

     uint8_t TcpState;

     /* 初始化网络协议栈 */

     init_TcpNet ();

     /* 创建一个周期是100ms的软定时器 */

    bsp_StartAutoTimer(, );

     while ()

     {

          /* TCP轮询 */

         tcpnet_poll();

         /* 按键消息的处理 */

         ucKeyCode = bsp_GetKey();

         if (ucKeyCode != KEY_NONE)

         {

              switch (ucKeyCode)

              {

                   /* K1键按下,TCP客户端1建立连接 */

                   case KEY_DOWN_K1:     

                       TcpState = tcp_get_state(socket_tcp1); //--------------(1)

                        /*

                          TCP_STATE_CONNECT是最后一个状态,未创建连接的话,会返回数值255,这里就是专门处理

                          这种状态,将其设置下一个TCP_STATE_CONNECT+1,即我们这里自定义了一个未创建连接的

                          状态。

                       */

                       if(TcpState > TCP_STATE_CONNECT)  //--------------(2)

                       {

                            TcpState = TCP_STATE_CONNECT + ;

                       }

                       printf_debug("TCP客户端1状态 = %s\r\n", ReVal_Table[TcpState]);

                       if(TcpState != TCP_STATE_CONNECT) //--------------(3)

                       {

                            TcpConnect_Client1(Rem_IP, PORT_NUM, LocalPort_NUM1);

                       }

                       break;

                   /* 其他的键值不处理 */

                   default:                    

                       break;

              }

         }

     }

}
  1. 优先检查TCP Socket的状态。
  2. TCP_STATE_CONNECT的状态定义在RTL.h文件:
    /* TCP States */
    
    #define TCP_STATE_FREE     0      /* Entry is free and unused                */
    
    #define TCP_STATE_CLOSED   1      /* Entry allocated, socket still closed    */
    
    #define TCP_STATE_LISTEN   2      /* Socket waiting for incoming connection  */
    
    #define TCP_STATE_SYN_REC  3      /* SYN frame received                      */
    
    #define TCP_STATE_SYN_SENT 4      /* SYN packet sent to establish a connect. */
    
    #define TCP_STATE_FINW1    5      /* Tcp_close started FIN packet was sent   */
    
    #define TCP_STATE_FINW2    6      /* Our FIN ack-ed, waiting for remote FIN  */
    
    #define TCP_STATE_CLOSING  7      /* Received FIN independently of our FIN   */
    
    #define TCP_STATE_LAST_ACK 8      /* Waiting for last ACK for our FIN        */
    
    #define TCP_STATE_TWAIT    9      /* Timed waiting for 2MSL                  */
    
    #define TCP_STATE_CONNECT  10     /* TCP Connection established              */

    从上面的定义中可以看出TCP_STATE_CONNECT是定义在了最后一个状态。由于未建立TCP连接的话,函数tcp_get_state返回的数值是255,这11种状态中未定义,所以专门再定义一个状态,表示未创建。数值比TCP_STATE_CONNECT大1,用在我们自己定义的数组ReVal_Table中。

  3. 未建立连接的话,调用函数TcpConnect_Client1创建连接。

15.5.2 TCP数据发送

TCP Socket的数据发送一定要注意各个函数调用顺序和使用方法,非常重要!否则,数据发送很容易失败。数据发送所用到函数的使用方法和注意事项在上一章的14.2小节有讲解。下面的代码中对数据发送专门做了处理,支持任意字节大小的数据发送,仅需修改计数变量iCount的初始值即可,初始值是多少,就发送多少字节。下面的代码是裸机方式的,测试发送4096字节:

/*

*********************************************************************************************************

*                                      外部变量和函数调用

*********************************************************************************************************

*/

extern uint8_t socket_tcp1;

extern void TcpConnect_Client1(uint8_t *_remip, uint16_t _remport, uint16_t _localport);

extern uint8_t socket_tcp2;

extern void TcpConnect_Client2(uint8_t *_remip, uint16_t _remport, uint16_t _localport);

extern uint8_t socket_tcp3;

extern void TcpConnect_Client3(uint8_t *_remip, uint16_t _remport, uint16_t _localport);

/*

*********************************************************************************************************

*                              宏定义,远程服务器的IP和端口

*********************************************************************************************************

*/

/* 要访问的远程服务器IP和端口配置,也就是电脑端调试助手设置的IP和端口号 */

#define IP1            192

#define IP2            168

#define IP3            1

#define IP4            2

#define PORT_NUM         1001

/* 这个是本地端口 */

#define LocalPort_NUM1    1024

#define LocalPort_NUM2    1025

#define LocalPort_NUM3    1026

/*

*********************************************************************************************************

*                                         变量

*********************************************************************************************************

*/

uint8_t Rem_IP[] = {IP1,IP2,IP3,IP4};

/* TCP状态的返回值 */

const char * ReVal_Table[]=

{

     "0: TCP_STATE_FREE",                                        

     "1: TCP_STATE_CLOSED",

     "2: TCP_STATE_LISTEN",

     "3: TCP_STATE_SYN_REC",

     "4: TCP_STATE_SYN_SENT",                                         

     "5: TCP_STATE_FINW1",

     "6: TCP_STATE_FINW2",

     "7: TCP_STATE_CLOSING",

     "8: TCP_STATE_LAST_ACK",                                         

     "9: TCP_STATE_TWAIT",

     "10: TCP_STATE_CONNECT",

     "11: 连接还未创建",

};

/*

*********************************************************************************************************

*    函 数 名: tcpnet_poll

*    功能说明: 使用TCPnet必须要一直调用的函数

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

void tcpnet_poll(void)

{

     if(bsp_CheckTimer())

     {

         bsp_LedToggle();

         /* 此函数坚决不可以放在中断里面跑 */

         timer_tick ();//--------------(1)

     }

     main_TcpNet ();//--------------(2)

}

/*

*********************************************************************************************************

*    函 数 名: TCPnetTest

*    功能说明: TCPnet应用

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

void TCPnetTest(void)

{ 

     int32_t iCount;

     uint8_t *sendbuf;

     uint16_t maxlen;

     uint8_t ucKeyCode;

     uint8_t TcpState;

     /* 初始化网络协议栈 */

     init_TcpNet ();

     /* 创建一个周期是100ms的软定时器 */

    bsp_StartAutoTimer(, );

     while ()

     {

         /* TCP轮询 */

         tcpnet_poll();

         /* 按键消息的处理 */

         ucKeyCode = bsp_GetKey();

         if (ucKeyCode != KEY_NONE)

         {

              switch (ucKeyCode)

              {

                   /* K1键按下,TCP客户端1建立连接 */

                   case KEY_DOWN_K1:     

                       TcpState = tcp_get_state(socket_tcp1);

                        /*

                          TCP_STATE_CONNECT是最后一个状态,未创建连接的话,会返回数值255,这里就是专门处理

                          这种状态,将其设置下一个TCP_STATE_CONNECT+1,即我们这里自定义了一个未创建连接的

                          状态。

                       */

                       if(TcpState > TCP_STATE_CONNECT)

                       {

                            TcpState = TCP_STATE_CONNECT + ;

                       }

                       printf_debug("TCP客户端1状态 = %s\r\n", ReVal_Table[TcpState]);

                       if(TcpState != TCP_STATE_CONNECT)

                       {

                            TcpConnect_Client1(Rem_IP, PORT_NUM, LocalPort_NUM1);

                       }

                       break;

                   /* 摇杆上键按下,TCP客户端1向TCP服务器发送4096字节数据 */

                   case JOY_DOWN_U:

                       TcpState = tcp_get_state(socket_tcp1);

                       printf_debug("TCP客户端1状态 = %s\r\n", ReVal_Table[TcpState]);                   

                       iCount = ; //--------------(3)

                       /* 保证已经建立连接了才可以发送数据 */

                       if(TcpState == TCP_STATE_CONNECT)

                       {

                            do   //--------------(4)

                            {

                                 tcpnet_poll();

                                 if (tcp_check_send (socket_tcp1) == __TRUE)

                                 {

                                     maxlen = tcp_max_dsize (socket_tcp1);

                                     iCount -= maxlen;

                                     if(iCount < )

                                     {

                                          /* 这么计算没问题的 */

                                          maxlen = iCount + maxlen;

                                     }

                                     /* 这里仅初始化了每次所发送数据包的前8个字节 */

                                     sendbuf = tcp_get_buf(maxlen);

                                     sendbuf[] = 'a';

                                     sendbuf[] = 'b';

                                     sendbuf[] = 'c';

                                     sendbuf[] = 'd';

                                     sendbuf[] = 'e';

                                     sendbuf[] = 'f';

                                     sendbuf[] = 'g';

                                     sendbuf[] = 'h';

                                      /* 测试发现只能使用获取的内存 */

                                     tcp_send (socket_tcp1, sendbuf, maxlen);

                                 }

                            }while(iCount > );        

                       }       

                       break;

                   /* 其他的键值不处理 */

                   default:                    

                       break;

              }

         }

     }

}
  1. 函数timer_tick用于实现网络时间基准,必须要周期性调用,周期大小是由配置向导文件中参数Tick Timer interval决定的。默认情况下,我们都取100ms。
  2. 函数main_TcpNet必须要一直调用着,协议栈的执行,主要靠它。
  3. 通过变量iCount设置要发送的字节数,这里是发送4096字节数据。
  4. do while语句中的流程很重要:

    (1)    函数tcp_poll一定要实时调用着。

    (2)    发送前务必要调用函数tcp_check_send查看发送是否就绪。

    (3)    函数tcp_max_dsize,tcp_get_buf和tcp_send务必要依次调用,一个都不能少。

说完了裸机方式,下面说说RTOS方式的数据发送,这里我们以RTX操作系统为例进行说明(其它的uCOS-III和FreeRTOS的思路是一样的)。RTX操作系统与裸机方式的主要不同是为RL-TCPnet专门配套了两个任务,一个是RL-TCPnet主任务,另一个是网络系统时间基准更新任务。

网络系统时间更新任务:

/*

*********************************************************************************************************

*    函 数 名: AppTaskStart

*    功能说明: 启动任务,也是最高优先级任务,这里实现RL-TCPnet的时间基准更新。

*    形    参: 无

*    返 回 值: 无

*   优 先 级: 5 

*********************************************************************************************************

*/

__task void AppTaskStart(void)

{

     /* 初始化RL-TCPnet */

     init_TcpNet ();

     /* 创建任务 */

     AppTaskCreate();

     os_itv_set ();

    while()

    {

         os_itv_wait ();

         /* RL-TCPnet时间基准更新函数 */

         timer_tick ();

    }

}

特别注意,这里的网络时间基准函数timer_tick,必须要周期性调用,周期大小是由配置向导文件中参数Tick Timer interval决定的。默认情况下,我们都取100ms,所以这里的延迟一定要匹配。

RL-TCPnet主任务,TCP数据收发在这个任务里面实现:

/*

*********************************************************************************************************

*    函 数 名: AppTaskTCPMain

*    功能说明: RL-TCPnet测试任务

*    形    参: 无

*    返 回 值: 无

*   优 先 级: 4 

*********************************************************************************************************

*/

__task void AppTaskTCPMain(void)

{

     while ()

     {

         TCPnetTest();

     }

}

函数TCPnetTest的具体实现如下:

/*

*********************************************************************************************************

*                              宏定义,远程服务器的IP和端口

*********************************************************************************************************

*/

/* 要访问的远程服务器IP和端口配置,也就是电脑端调试助手设置的IP和端口号 */

#define IP1            192

#define IP2            168

#define IP3            1

#define IP4            2

#define PORT_NUM         1001

/* 这个是本地端口 */

#define LocalPort_NUM1    1024

#define LocalPort_NUM2    1025

#define LocalPort_NUM3    1026

/*

*********************************************************************************************************

*                                         变量

*********************************************************************************************************

*/

uint8_t Rem_IP[] = {IP1,IP2,IP3,IP4};

/* TCP状态的返回值 */

const char * ReVal_Table[]=

{

     "0: TCP_STATE_FREE",                                        

     "1: TCP_STATE_CLOSED",

     "2: TCP_STATE_LISTEN",

     "3: TCP_STATE_SYN_REC",

     "4: TCP_STATE_SYN_SENT",                                         

     "5: TCP_STATE_FINW1",

     "6: TCP_STATE_FINW2",

     "7: TCP_STATE_CLOSING",

     "8: TCP_STATE_LAST_ACK",                                         

     "9: TCP_STATE_TWAIT",

     "10: TCP_STATE_CONNECT",

     "11: 连接还未创建",

};

/*

*********************************************************************************************************

*    函 数 名: TCPnetTest

*    功能说明: TCPnet应用

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

void TCPnetTest(void)

{ 

     int32_t iCount;

     uint8_t *sendbuf;

     uint16_t maxlen;

     uint8_t TcpState;

     OS_RESULT xResult;

     const uint16_t usMaxBlockTime = ; /* 延迟周期 */

     while ()

     {

         /* RL-TCPnet处理函数 */

         main_TcpNet();//--------------(1)

         /* 按键消息的处理 */

         if(os_evt_wait_or(0xFFFF, usMaxBlockTime) == OS_R_EVT) //--------------(2)

         {

              xResult = os_evt_get ();

              switch (xResult)

              {

                   /* 接收到K1键按下,TCP客户端1建立连接 */

                   case KEY1_BIT0:              

                       TcpState = tcp_get_state(socket_tcp1);

                        /*

                          TCP_STATE_CONNECT是最后一个状态,未创建连接的话,会返回数值255,这里就是专门处理

                          这种状态,将其设置下一个TCP_STATE_CONNECT+1,即我们这里自定义了一个未创建连接的

                          状态。

                       */

                       if(TcpState > TCP_STATE_CONNECT)

                       {

                            TcpState = TCP_STATE_CONNECT + ;

                       }

                       printf_debug("TCP客户端1状态 = %s\r\n", ReVal_Table[TcpState]);

                       if(TcpState != TCP_STATE_CONNECT)

                       {

                            TcpConnect_Client1(Rem_IP, PORT_NUM, LocalPort_NUM1);

                       }

                        break;

                   /* 接收到摇杆上键按下,TCP客户端1向TCP服务器发送4096字节数据 */

                   case JOY_U_BIT3:             

                       TcpState = tcp_get_state(socket_tcp1);

                       printf_debug("TCP客户端1状态 = %s\r\n", ReVal_Table[TcpState]);                   

                       iCount = ; //--------------(3)

                       /* 保证已经建立连接了才可以发送数据 */

                       if(TcpState == TCP_STATE_CONNECT) //--------------(4)

                       {

                            do   //--------------(5)

                            {

                                 main_TcpNet();

                                 if (tcp_check_send (socket_tcp1) == __TRUE)

                                 {

                                     maxlen = tcp_max_dsize (socket_tcp1);

                                     iCount -= maxlen;

                                     if(iCount < )

                                     {

                                          /* 这么计算没问题的 */

                                          maxlen = iCount + maxlen;

                                     }

                                      /* 这里仅初始化了每次所发送数据包的前8个字节 */

                                     sendbuf = tcp_get_buf(maxlen);

                                     sendbuf[] = 'a';

                                     sendbuf[] = 'b';

                                     sendbuf[] = 'c';

                                     sendbuf[] = 'd';

                                     sendbuf[] = 'e';

                                     sendbuf[] = 'f';

                                     sendbuf[] = 'g';

                                     sendbuf[] = 'h';

                                      /* 测试发现只能使用获取的内存 */

                                     tcp_send (socket_tcp1, sendbuf, maxlen);

                                 }

                            }while(iCount > );        

                       }       

                       break;                

                   /* 其他的键值不处理 */

                   default:                    

                       break;

              }

         }

     }

}
  1. 函数main_TcpNet必须要一直调用着,协议栈的执行,主要靠它。
  2. 这里使用了事件标志组,溢出时间设置为了2毫秒。这样一方面保证了函数main_TcpNet的周期性执行,另一方面用来等待按键发送事件标志消息。
  3. 通过变量iCount设置要发送的字节数,这里是发送4096字节数据。
  4. 这里是为了保证已经建立连接了才可以发送数据。
  5. do while语句中的流程很重要:

    (1)    函数main_TcpNet一定要实时调用着。

    (2)    发送前务必要调用函数tcp_check_send查看发送是否就绪。

    (3)    函数tcp_max_dsize,tcp_get_buf和tcp_send务必要以此调用,一个都不能少。

15.5.3 TCP数据接收

TCP数据接收主要是通过函数tcp_get_socket的回调函数实现(裸机,RTX,uCOS-III和FreeRTOS是一样的),下面以TCP客户端1的数据接收为例进行说明:

/*

*********************************************************************************************************

*    函 数 名: tcp_callback

*    功能说明: TCP Socket的回调函数

*    形    参: soc  TCP Socket类型

*             evt  事件类型

*             ptr  事件类型是TCP_EVT_DATA,ptr指向的缓冲区记录着接收到的TCP数据,其余事件记录IP地址

*             par  事件类型是TCP_EVT_DATA,记录接收到的数据个数,其余事件记录端口号

*    返 回 值: 主要是TCP_EVT_CONREQ事件使用,返回数值1允许连接,返回数值0禁止连接。

*********************************************************************************************************

*/

static U16 tcp_callback1 (U8 soc, U8 evt, U8 *ptr, U16 par)

{

     char buf[];

     uint16_t i;

     /* 确保是socket_tcp的回调 */

     if (soc != socket_tcp1)

     {

         return ();

     }

     switch (evt)

     {

         /*

              远程客户端连接消息

             1、数组ptr存储远程设备的IP地址,par中存储端口号。

             2、返回数值1允许连接,返回数值0禁止连接。

         */

         case TCP_EVT_CONREQ:

              sprintf(buf, "远程客户端请求连接IP: %d.%d.%d.%d", ptr[], ptr[], ptr[], ptr[]);

              printf_debug1("IP:%s  port:%d\r\n", buf, par);

              return ();

         /* 连接终止 */

         case TCP_EVT_ABORT:

              break;

         /* Socket远程连接已经建立 */

         case TCP_EVT_CONNECT:

              printf_debug1("Socket is connected to remote peer\r\n");

              break;

          /* 连接断开 */

         case TCP_EVT_CLOSE:

              printf_debug1("Connection has been closed\r\n");

              break;

         /* 发送的数据收到远程设备应答 */

         case TCP_EVT_ACK:

              break;

         /* 接收到TCP数据帧,ptr指向数据地址,par记录数据长度,单位字节 */

         case TCP_EVT_DATA: //--------------(1)

              printf_debug1("TCP客户端1接收到数据长度 = %d\r\n", par);

              for(i = ; i < par; i++)

              {

                   printf_debug1("ptr[%d] = %d\r\n", i, ptr[i]);

              }

              break;

     }

     return ();

}
  1. TCP服务器的数据接收主要是通过回调函数的TCP_EVT_DATA消息实现,进入消息后,指针变量ptr是接收数据缓冲区首地址,变量par记录接收到的数据长度,单位字节。

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

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

http://bbs.armfly.com/read.php?tid=1568

重要提示,操作的过程中务必要优先在电脑端创建TCP服务器并开启,然后再操作板子进行连接。因为本章节配套的实例在按键按下后调用函数tcp_connect只进行一次连接,如果在Net_Config.c文件中配置的重连次数范围内无法连接上,就不会再进行连接了,需要再次点击按键进行连接。

15.6.1 获取板子IP地址

(说明,对于TCP客户端实验,这步已经不需要了,不过大家还可以进行测试)

首先,强烈推荐将网线接到路由器或者交换机上面测试,因为已经使能了DHCP,可以自动获取IP地址,而且在前面的配置中使能了局域网域名NetBIOS,用户只需在电脑端ping armfly就可以获得板子的IP地址。测试方法如下:

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

【RL-TCPnet网络教程】第15章 RL-TCPnet之创建多个TCP连接

(2)弹出的命令窗口中,输入ping armfly。

【RL-TCPnet网络教程】第15章 RL-TCPnet之创建多个TCP连接

(3)输入ping armfly后,回车。

【RL-TCPnet网络教程】第15章 RL-TCPnet之创建多个TCP连接

获得IP地址是192.168.1.6。

15.6.2 获取电脑的IP地址

获取电脑IP地址的方法很多,可以在网上邻居获取,也可以通过输入命令ipconfig获取,方法跟上面15.6.1小节中的方式一样:

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

【RL-TCPnet网络教程】第15章 RL-TCPnet之创建多个TCP连接

(2)弹出的命令窗口中,输入ipconfig。

【RL-TCPnet网络教程】第15章 RL-TCPnet之创建多个TCP连接

(3)输入ipconfig后,回车。

【RL-TCPnet网络教程】第15章 RL-TCPnet之创建多个TCP连接

获得电脑的IP地址是192.168.1.2。

15.6.3 在程序中配置要访问的远程IP地址和端口

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

/*

*********************************************************************************************************

*                              宏定义,远程服务器的IP和端口

*********************************************************************************************************

*/

/* 要访问的远程服务器IP和端口配置,也就是电脑端调试助手设置的IP和端口号 */

#define IP1            192

#define IP2            168

#define IP3            1

#define IP4            2

#define PORT_NUM         1001

15.6.4 网络调试助手创建TCP服务器

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

【RL-TCPnet网络教程】第15章 RL-TCPnet之创建多个TCP连接

2、弹出如下界面,指定IP设置为192.168.1.2,一定要跟15.6.2小节中获得的电脑IP地址一致,端口号1001,最后点击确定:

【RL-TCPnet网络教程】第15章 RL-TCPnet之创建多个TCP连接

3、确定后的界面效果如下:

【RL-TCPnet网络教程】第15章 RL-TCPnet之创建多个TCP连接

4、然后点击启动服务器:

【RL-TCPnet网络教程】第15章 RL-TCPnet之创建多个TCP连接

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

【RL-TCPnet网络教程】第15章 RL-TCPnet之创建多个TCP连接

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

#define LocalPort_NUM1    1024 是一致的。IP地址也跟15.6.1小节中获取的IP地址也是一致的。同样的方法,按下K2按键,可以看到TCP客户端2也加入了连接,端口号是1025:

【RL-TCPnet网络教程】第15章 RL-TCPnet之创建多个TCP连接

按下K3按键,可以看到TCP客户端3也加入了连接,端口号1026:

【RL-TCPnet网络教程】第15章 RL-TCPnet之创建多个TCP连接

三个都连接上后,串口软件也会打印出如下信息(波特率115200,数据位8,奇偶校验位无,停止位1):

【RL-TCPnet网络教程】第15章 RL-TCPnet之创建多个TCP连接

15.6.5 TCP客户端发送数据

板子和网络调试助手建立连接后就可以互相收发数据了。对于发送数据,三个TCP客户端都可以给服务器发送数据。

(1)摇杆上键按下,TCP客户端1发送4096字节,每次发送数据包的前8个字节设置了字符a到字符h,后面都未做设置。

【RL-TCPnet网络教程】第15章 RL-TCPnet之创建多个TCP连接

(2)摇杆左键按下,TCP客户端2发送4096字节,每次发送数据包的前8个字节设置了字符a到字符h,后面都未做设置。

【RL-TCPnet网络教程】第15章 RL-TCPnet之创建多个TCP连接

(3)摇杆右键按下,TCP客户端3发送4096字节,每次发送数据包的前8个字节设置了字符a到字符h,后面都未做设置。

【RL-TCPnet网络教程】第15章 RL-TCPnet之创建多个TCP连接

15.6.6 TCP客户端接收数据

TCP服务器接收数据的测试也比较方便,我们这里通过网络调试助手给板子发送1到5,共5个字符。

(1)TCP客户端1数据接收测试。

【RL-TCPnet网络教程】第15章 RL-TCPnet之创建多个TCP连接

点击发送后,可以看到串口软件打印出接收到的5个字符:

【RL-TCPnet网络教程】第15章 RL-TCPnet之创建多个TCP连接

字符1对应的ASCII值就是49,其它字符数值依次增加。测试也是没问题的。

(2)TCP客户端2数据接收测试。

【RL-TCPnet网络教程】第15章 RL-TCPnet之创建多个TCP连接

点击发送后,可以看到串口软件打印出接收到的5个字符:

【RL-TCPnet网络教程】第15章 RL-TCPnet之创建多个TCP连接

字符1对应的ASCII值就是49,其它字符数值依次增加。测试也是没问题的。

(3)TCP客户端3数据接收测试。

【RL-TCPnet网络教程】第15章 RL-TCPnet之创建多个TCP连接

点击发送后,可以看到串口软件打印出接收到的5个字符:

【RL-TCPnet网络教程】第15章 RL-TCPnet之创建多个TCP连接

字符1对应的ASCII值就是49,其它字符数值依次增加。测试也是没问题的。

15.7 实验例程说明(裸机)

15.7.1 STM32F407开发板实验

配套例子:

V5-1016_RL-TCPnet实验_多个TCP客户端连接(裸机)

实验目的:

  1. 学习RL-TCPnet的多个TCP客户端创建和数据收发。

实验内容:

  1. 强烈推荐将网线接到路由器或者交换机上面测试,因为已经使能了DHCP,可以自动获取IP地址。
  2. 客户端的例子相比服务器的例子稍麻烦些,因为客户端的例子需要用户知道电脑端IP和端口号。并根据实际情况设置IP和端口号的宏定义,这个配置在文件app_tcpnet_lib.c开头,测试的时候板子要连接这个IP和端口(下面是默认配置,一定要根据实际情况重新配置,如果不会配置,看本例程对应的教程即可):

    #define IP1            192

    #define IP2            168

    #define IP3            1

    #define IP4            2

    #define PORT_NUM       1001

  3. 本例程可以创建三个TCP Client,而且使能了局域网域名NetBIOS,用户只需在电脑端ping armfly就可以获得板子的IP地址,三个TCP Client的端口号分别是1024,1025和1026。
  4. 用户可以在电脑端用网络调试软件创建TCP Server跟这三个客户端建立连接。执行下面5--10步的操作时,优先将电脑端的TCP Server建立起来!
  5. 按键K1按下,创建TCP客户端1,并跟电脑端的TCP服务器建立连接。
  6. 按键K2按下,创建TCP客户端2,并跟电脑端的TCP服务器建立连接。
  7. 按键K3按下,创建TCP客户端3,并跟电脑端的TCP服务器建立连接。
  8. 摇杆上键按下,TCP客户端1给电脑端的TCP服务器发送4096字节数据。
  9. 摇杆左键按下,TCP客户端2给电脑端的TCP服务器发送4096字节数据。
  10. 摇杆右键按下,TCP客户端3给电脑端的TCP服务器发送4096字节数据。

实验操作:

详见本章节15.6小节。

配置向导文件设置(Net_Config.c):

详见本章节15.3小节。

调试文件设置(Net_Debug.c):

详见本章节15.4小节。

程序设计:

主函数初始化

在main.c文件实现:

/*

*********************************************************************************************************

*    函 数 名: main

*    功能说明: 标准c程序入口。

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

int main (void)

{   

     /* 初始化外设 */

     bsp_Init();

     /* 进入RL-TCPnet测试函数 */

     TCPnetTest();

}

硬件外设初始化

硬件外设的初始化是在 bsp.c 文件实现:

/*

*********************************************************************************************************

*    函 数 名: bsp_Init

*    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次

*    形    参:无

*    返 回 值: 无

*********************************************************************************************************

*/

void bsp_Init(void)

{

     /*

         由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。

         启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。

         系统时钟缺省配置为168MHz,如果需要更改,可以修改 system_stm32f4xx.c 文件

     */

     /* 优先级分组设置为4,可配置0-15级抢占式优先级,0级子优先级,即不存在子优先级。*/

     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

     bsp_InitUart();    /* 初始化串口 */

     bsp_InitKey();     /* 初始化按键变量(必须在 bsp_InitTimer() 之前调用) */

     bsp_InitLed();     /* 初始LED指示灯端口 */

     bsp_InitTimer();   /* 初始化滴答定时器 */

}

三个TCP客户端的实现

三个TCP客户端实现都是一样的,我们这里仅以客户端1为例进行说明:

#include "bsp.h"      

#include <RTL.h>

#include <stdio.h>

#include <Net_Config.h>

/*

*********************************************************************************************************

*                                      用于本文件的调试

*********************************************************************************************************

*/

#if 1

     #define printf_debug1 printf

#else

     #define printf_debug1(...)

#endif

/*

*********************************************************************************************************

*                                         变量

*********************************************************************************************************

*/

uint8_t socket_tcp1;

/*

*********************************************************************************************************

*    函 数 名: tcp_callback

*    功能说明: TCP Socket的回调函数

*    形    参: soc  TCP Socket类型

*             evt  事件类型

*             ptr  事件类型是TCP_EVT_DATA,ptr指向的缓冲区记录着接收到的TCP数据,其余事件记录IP地址

*             par  事件类型是TCP_EVT_DATA,记录接收到的数据个数,其余事件记录端口号

*    返 回 值: 主要是TCP_EVT_CONREQ事件使用,返回数值1允许连接,返回数值0禁止连接。

*********************************************************************************************************

*/

static U16 tcp_callback1 (U8 soc, U8 evt, U8 *ptr, U16 par)

{

     char buf[];

     uint16_t i;

     /* 确保是socket_tcp的回调 */

     if (soc != socket_tcp1)

     {

         return ();

     }

     switch (evt)

     {

         /*

              远程客户端连接消息

             1、数组ptr存储远程设备的IP地址,par中存储端口号。

             2、返回数值1允许连接,返回数值0禁止连接。

         */

         case TCP_EVT_CONREQ:

              sprintf(buf, "远程客户端请求连接IP: %d.%d.%d.%d", ptr[], ptr[], ptr[], ptr[]);

              printf_debug1("IP:%s  port:%d\r\n", buf, par);

              return ();

          /* 连接终止 */

         case TCP_EVT_ABORT:

              break;

          /* Socket远程连接已经建立 */

         case TCP_EVT_CONNECT:

              printf_debug1("Socket is connected to remote peer\r\n");

              break;

          /* 连接断开 */

         case TCP_EVT_CLOSE:

              printf_debug1("Connection has been closed\r\n");

              break;

         /* 发送的数据收到远程设备应答 */

         case TCP_EVT_ACK:

              break;

         /* 接收到TCP数据帧,ptr指向数据地址,par记录数据长度,单位字节 */

         case TCP_EVT_DATA:

              printf_debug1("TCP客户端1接收到数据长度 = %d\r\n", par);

              for(i = ; i < par; i++)

              {

                   printf_debug1("ptr[%d] = %d\r\n", i, ptr[i]);

              }

              break;

     }

     return ();

}

/*

*********************************************************************************************************

*    函 数 名: TcpConnect_Client1

*    功能说明: 创建一个TCP Client

*    形    参: _remip     要访问的远程IP地址

*             _remport   远程端口号

*             _localport 本地端口号

*    返 回 值: 无

*********************************************************************************************************

*/

void TcpConnect_Client1(uint8_t *_remip, uint16_t _remport, uint16_t _localport)

{

     uint8_t res;

     /*

        创建TCP Socket并连接,客户端连接服务器后,10秒内无数据通信将断开连接。

        但是由于这里使能了TCP_TYPE_KEEP_ALIVE,会一直保持连接,不受10秒的时间限制。

     */

    socket_tcp1 = tcp_get_socket (TCP_TYPE_CLIENT | TCP_TYPE_KEEP_ALIVE, , , tcp_callback1);

     if(socket_tcp1 != )

     {

         res = tcp_connect (socket_tcp1, _remip, _remport, _localport);

         printf_debug1("TCP Socket创建成功res = %d\r\n", res);

     }

}

RL-TCPnet功能测试

这里专门创建了一个app_tcpnet_lib.c文件用于RL-TCPnet功能的测试。

#include "bsp.h"      

#include <RTL.h>

#include <stdio.h>

#include <Net_Config.h>

/*

*********************************************************************************************************

*                                      用于本文件的调试

*********************************************************************************************************

*/

#if 1

     #define printf_debug printf

#else

     #define printf_debug(...)

#endif

/*

*********************************************************************************************************

*                                      外部变量和函数调用

*********************************************************************************************************

*/

extern uint8_t socket_tcp1;

extern void TcpConnect_Client1(uint8_t *_remip, uint16_t _remport, uint16_t _localport);

extern uint8_t socket_tcp2;

extern void TcpConnect_Client2(uint8_t *_remip, uint16_t _remport, uint16_t _localport);

extern uint8_t socket_tcp3;

extern void TcpConnect_Client3(uint8_t *_remip, uint16_t _remport, uint16_t _localport);

/*

*********************************************************************************************************

*                              宏定义,远程服务器的IP和端口

*********************************************************************************************************

*/

/* 要访问的远程服务器IP和端口配置,也就是电脑端调试助手设置的IP和端口号 */

#define IP1            192

#define IP2            168

#define IP3            1

#define IP4            2

#define PORT_NUM         1001

/* 这个是本地端口 */

#define LocalPort_NUM1    1024

#define LocalPort_NUM2    1025

#define LocalPort_NUM3    1026

/*

*********************************************************************************************************

*                                         变量

*********************************************************************************************************

*/

uint8_t Rem_IP[] = {IP1,IP2,IP3,IP4};

/* TCP状态的返回值 */

const char * ReVal_Table[]=

{

     "0: TCP_STATE_FREE",                                        

     "1: TCP_STATE_CLOSED",

     "2: TCP_STATE_LISTEN",

     "3: TCP_STATE_SYN_REC",

     "4: TCP_STATE_SYN_SENT",                                         

     "5: TCP_STATE_FINW1",

     "6: TCP_STATE_FINW2",

     "7: TCP_STATE_CLOSING",

     "8: TCP_STATE_LAST_ACK",                                         

     "9: TCP_STATE_TWAIT",

     "10: TCP_STATE_CONNECT",

     "11: 连接还未创建",

};

/*

*********************************************************************************************************

*    函 数 名: tcpnet_poll

*    功能说明: 使用TCPnet必须要一直调用的函数

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

void tcpnet_poll(void)

{

     if(bsp_CheckTimer())

     {

         bsp_LedToggle();

         /* 此函数坚决不可以放在中断里面跑 */

         timer_tick ();

     }

     main_TcpNet ();

}

/*

*********************************************************************************************************

*    函 数 名: TCPnetTest

*    功能说明: TCPnet应用

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

void TCPnetTest(void)

{ 

     int32_t iCount;

     uint8_t *sendbuf;

     uint16_t maxlen;

     uint8_t ucKeyCode;

     uint8_t TcpState;

     /* 初始化网络协议栈 */

     init_TcpNet ();

     /* 创建一个周期是100ms的软定时器 */

    bsp_StartAutoTimer(, );

     while ()

     {

          /* TCP轮询 */

         tcpnet_poll();

          /* 按键消息的处理 */

         ucKeyCode = bsp_GetKey();

         if (ucKeyCode != KEY_NONE)

         {

              switch (ucKeyCode)

              {

                   /* K1键按下,TCP客户端1建立连接 */

                   case KEY_DOWN_K1:     

                       TcpState = tcp_get_state(socket_tcp1);

                        /*

                          TCP_STATE_CONNECT是最后一个状态,未创建连接的话,会返回数值255,这里就是专门处理

                          这种状态,将其设置下一个TCP_STATE_CONNECT+1,即我们这里自定义了一个未创建连接的

                          状态。

                       */

                       if(TcpState > TCP_STATE_CONNECT)

                       {

                            TcpState = TCP_STATE_CONNECT + ;

                       }

                       printf_debug("TCP客户端1状态 = %s\r\n", ReVal_Table[TcpState]);

                       if(TcpState != TCP_STATE_CONNECT)

                       {

                            TcpConnect_Client1(Rem_IP, PORT_NUM, LocalPort_NUM1);

                       }

                       break;

                   /* K2键按下,TCP客户端2建立连接 */

                   case KEY_DOWN_K2:     

                       TcpState = tcp_get_state(socket_tcp2);

                        /*

                          TCP_STATE_CONNECT是最后一个状态,未创建连接的话,会返回数值255,这里就是专门处理

                          这种状态,将其设置下一个TCP_STATE_CONNECT+1,即我们这里自定义了一个未创建连接的

                          状态。

                       */

                       if(TcpState > TCP_STATE_CONNECT)

                       {

                            TcpState = TCP_STATE_CONNECT + ;

                       }

                       printf_debug("TCP客户端2状态 = %s\r\n", ReVal_Table[TcpState]);

                       if(TcpState != TCP_STATE_CONNECT)

                       {

                            TcpConnect_Client2(Rem_IP, PORT_NUM, LocalPort_NUM2);

                       }   

                       break;

                   /* K3键按下,TCP客户端3建立连接 */

                   case KEY_DOWN_K3:            

                       TcpState = tcp_get_state(socket_tcp3);

                        /*

                          TCP_STATE_CONNECT是最后一个状态,未创建连接的话,会返回数值255,这里就是专门处理

                          这种状态,将其设置下一个TCP_STATE_CONNECT+1,即我们这里自定义了一个未创建连接的

                          状态。

                       */

                       if(TcpState > TCP_STATE_CONNECT)

                       {

                            TcpState = TCP_STATE_CONNECT + ;

                       }

                       printf_debug("TCP客户端3状态 = %s\r\n", ReVal_Table[TcpState]);

                       if(TcpState != TCP_STATE_CONNECT)

                        {

                            TcpConnect_Client3(Rem_IP, PORT_NUM, LocalPort_NUM3);

                       }   

                       break;

                   /* 摇杆上键按下,TCP客户端1向TCP服务器发送4096字节数据 */

                   case JOY_DOWN_U:

                       TcpState = tcp_get_state(socket_tcp1);

                       printf_debug("TCP客户端1状态 = %s\r\n", ReVal_Table[TcpState]);                   

                       iCount = ;

                       /* 保证已经建立连接了才可以发送数据 */

                       if(TcpState == TCP_STATE_CONNECT)

                       {

                            do

                            {

                                 tcpnet_poll();

                                 if (tcp_check_send (socket_tcp1) == __TRUE)

                                 {

                                     maxlen = tcp_max_dsize (socket_tcp1);

                                     iCount -= maxlen;

                                     if(iCount < )

                                     {

                                          /* 这么计算没问题的 */

                                          maxlen = iCount + maxlen;

                                     }

                                     /* 这里仅初始化了每次所发送数据包的前8个字节 */

                                     sendbuf = tcp_get_buf(maxlen);

                                     sendbuf[] = 'a';

                                     sendbuf[] = 'b';

                                     sendbuf[] = 'c';

                                     sendbuf[] = 'd';

                                     sendbuf[] = 'e';

                                     sendbuf[] = 'f';

                                     sendbuf[] = 'g';

                                     sendbuf[] = 'h';

                                      /* 测试发现只能使用获取的内存 */

                                     tcp_send (socket_tcp1, sendbuf, maxlen);

                                 }

                            }while(iCount > );        

                       }       

                       break;

                   /* 摇杆左键按下,TCP客户端2向TCP服务器发送4096字节数据 */

                   case JOY_DOWN_L:

                       TcpState = tcp_get_state(socket_tcp2);

                       printf_debug("TCP客户端2状态 = %s\r\n", ReVal_Table[TcpState]);                   

                       iCount = ;

                       /* 保证已经建立连接了才可以发送数据 */

                       if(TcpState == TCP_STATE_CONNECT)

                       {

                            do

                            {

                                 tcpnet_poll();

                                 if (tcp_check_send (socket_tcp2) == __TRUE)

                                 {

                                     maxlen = tcp_max_dsize (socket_tcp2);

                                     iCount -= maxlen;

                                     if(iCount < )

                                     {

                                          /* 这么计算没问题的 */

                                          maxlen = iCount + maxlen;

                                     }

                                      /* 这里仅初始化了每次所发送数据包的前8个字节 */

                                     sendbuf = tcp_get_buf(maxlen);

                                     sendbuf[] = 'a';

                                     sendbuf[] = 'b';

                                     sendbuf[] = 'c';

                                     sendbuf[] = 'd';

                                     sendbuf[] = 'e';

                                     sendbuf[] = 'f';

                                     sendbuf[] = 'g';

                                     sendbuf[] = 'h';

                                     /* 测试发现只能使用获取的内存 */

                                     tcp_send (socket_tcp2, sendbuf, maxlen);

                                }

                            }while(iCount > );        

                       }       

                       break;

                   /* 摇杆右键按下,TCP客户端3向TCP服务器发送4096字节数据 */

                   case JOY_DOWN_R:

                       TcpState = tcp_get_state(socket_tcp3);

                       printf_debug("TCP客户端3状态 = %s\r\n", ReVal_Table[TcpState]);                   

                       iCount = ;

                       /* 保证已经建立连接了才可以发送数据 */

                       if(TcpState == TCP_STATE_CONNECT)

                       {

                            do

                            {

                                 tcpnet_poll();

                                 if (tcp_check_send (socket_tcp3) == __TRUE)

                                 {

                                     maxlen = tcp_max_dsize (socket_tcp3);

                                     iCount -= maxlen;

                                     if(iCount < )

                                     {

                                          /* 这么计算没问题的 */

                                          maxlen = iCount + maxlen;

                                     }

                                      /* 这里仅初始化了每次所发送数据包的前8个字节 */

                                     sendbuf = tcp_get_buf(maxlen);

                                     sendbuf[] = 'a';

                                     sendbuf[] = 'b';

                                      sendbuf[] = 'c';

                                     sendbuf[] = 'd';

                                     sendbuf[] = 'e';

                                     sendbuf[] = 'f';

                                     sendbuf[] = 'g';

                                     sendbuf[] = 'h';

                                      /* 测试发现只能使用获取的内存 */

                                     tcp_send (socket_tcp3, sendbuf, maxlen);

                                 }

                            }while(iCount > );        

                       }       

                       break;

                   /* 其他的键值不处理 */

                   default:                    

                       break;

              }

         }

     }

}

15.7.2 STM32F429开发板实验

配套例子:

V6-1016_RL-TCPnet实验_多个TCP客户端连接(裸机)

实验目的:

  1. 学习RL-TCPnet的多个TCP客户端创建和数据收发。

实验内容:

  1. 强烈推荐将网线接到路由器或者交换机上面测试,因为已经使能了DHCP,可以自动获取IP地址。
  2. 客户端的例子相比服务器的例子稍麻烦些,因为客户端的例子需要用户知道电脑端IP和端口号。并根据实际情况设置IP和端口号的宏定义,这个配置在文件app_tcpnet_lib.c开头,测试的时候板子要连接这个IP和端口(下面是默认配置,一定要根据实际情况重新配置,如果不会配置,看本例程对应的教程即可):

    #define IP1            192

    #define IP2            168

    #define IP3            1

    #define IP4            2

    #define PORT_NUM       1001

  3. 本例程可以创建三个TCP Client,而且使能了局域网域名NetBIOS,用户只需在电脑端ping armfly就可以获得板子的IP地址,三个TCP Client的端口号分别是1024,1025和1026。
  4. 用户可以在电脑端用网络调试软件创建TCP Server跟这三个客户端建立连接。执行下面5--10步的操作时,优先将电脑端的TCP Server建立起来!
  5. 按键K1按下,创建TCP客户端1,并跟电脑端的TCP服务器建立连接。
  6. 按键K2按下,创建TCP客户端2,并跟电脑端的TCP服务器建立连接。
  7. 按键K3按下,创建TCP客户端3,并跟电脑端的TCP服务器建立连接。
  8. 摇杆上键按下,TCP客户端1给电脑端的TCP服务器发送4096字节数据。
  9. 摇杆左键按下,TCP客户端2给电脑端的TCP服务器发送4096字节数据。
  10. 摇杆右键按下,TCP客户端3给电脑端的TCP服务器发送4096字节数据。

实验操作:

详见本章节15.6小节。

配置向导文件设置(Net_Config.c):

详见本章节15.3小节。

调试文件设置(Net_Debug.c):

详见本章节15.4小节。

程序设计:

主函数初始化

在main.c文件实现:

/*

*********************************************************************************************************

*    函 数 名: main

*    功能说明: 标准c程序入口。

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

int main (void)

{   

     /* 初始化外设 */

     bsp_Init();

     /* 进入RL-TCPnet测试函数 */

     TCPnetTest();

}

硬件外设初始化

硬件外设的初始化是在 bsp.c 文件实现:

/*

*********************************************************************************************************

*    函 数 名: bsp_Init

*    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次

*    形    参:无

*    返 回 值: 无

*********************************************************************************************************

*/

void bsp_Init(void)

{

     /*

         由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。

         启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。

         系统时钟缺省配置为168MHz,如果需要更改,可以修改 system_stm32f4xx.c 文件

     */

     /* 优先级分组设置为4,可配置0-15级抢占式优先级,0级子优先级,即不存在子优先级。*/

     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

     bsp_InitUart();    /* 初始化串口 */

     bsp_InitKey();     /* 初始化按键变量(必须在 bsp_InitTimer() 之前调用) */

     bsp_InitLed();     /* 初始LED指示灯端口 */

     bsp_InitTimer();   /* 初始化滴答定时器 */

}

三个TCP客户端的实现

三个TCP客户端实现都是一样的,我们这里仅以客户端1为例进行说明:

#include "bsp.h"      

#include <RTL.h>

#include <stdio.h>

#include <Net_Config.h>

/*

*********************************************************************************************************

*                                      用于本文件的调试

*********************************************************************************************************

*/

#if 1

     #define printf_debug1 printf

#else

     #define printf_debug1(...)

#endif

/*

*********************************************************************************************************

*                                         变量

*********************************************************************************************************

*/

uint8_t socket_tcp1;

/*

*********************************************************************************************************

*    函 数 名: tcp_callback

*    功能说明: TCP Socket的回调函数

*    形    参: soc  TCP Socket类型

*             evt  事件类型

*             ptr  事件类型是TCP_EVT_DATA,ptr指向的缓冲区记录着接收到的TCP数据,其余事件记录IP地址

*             par  事件类型是TCP_EVT_DATA,记录接收到的数据个数,其余事件记录端口号

*    返 回 值: 主要是TCP_EVT_CONREQ事件使用,返回数值1允许连接,返回数值0禁止连接。

*********************************************************************************************************

*/

static U16 tcp_callback1 (U8 soc, U8 evt, U8 *ptr, U16 par)

{

     char buf[];

     uint16_t i;

     /* 确保是socket_tcp的回调 */

     if (soc != socket_tcp1)

     {

         return ();

     }

     switch (evt)

     {

         /*

              远程客户端连接消息

             1、数组ptr存储远程设备的IP地址,par中存储端口号。

             2、返回数值1允许连接,返回数值0禁止连接。

         */

         case TCP_EVT_CONREQ:

              sprintf(buf, "远程客户端请求连接IP: %d.%d.%d.%d", ptr[], ptr[], ptr[], ptr[]);

              printf_debug1("IP:%s  port:%d\r\n", buf, par);

              return ();

          /* 连接终止 */

         case TCP_EVT_ABORT:

              break;

          /* Socket远程连接已经建立 */

         case TCP_EVT_CONNECT:

              printf_debug1("Socket is connected to remote peer\r\n");

              break;

          /* 连接断开 */

         case TCP_EVT_CLOSE:

              printf_debug1("Connection has been closed\r\n");

              break;

         /* 发送的数据收到远程设备应答 */

         case TCP_EVT_ACK:

              break;

         /* 接收到TCP数据帧,ptr指向数据地址,par记录数据长度,单位字节 */

         case TCP_EVT_DATA:

              printf_debug1("TCP客户端1接收到数据长度 = %d\r\n", par);

              for(i = ; i < par; i++)

              {

                   printf_debug1("ptr[%d] = %d\r\n", i, ptr[i]);

              }

              break;

     }

     return ();

}

/*

*********************************************************************************************************

*    函 数 名: TcpConnect_Client1

*    功能说明: 创建一个TCP Client

*    形    参: _remip     要访问的远程IP地址

*             _remport   远程端口号

*             _localport 本地端口号

*    返 回 值: 无

*********************************************************************************************************

*/

void TcpConnect_Client1(uint8_t *_remip, uint16_t _remport, uint16_t _localport)

{

     uint8_t res;

     /*

        创建TCP Socket并连接,客户端连接服务器后,10秒内无数据通信将断开连接。

        但是由于这里使能了TCP_TYPE_KEEP_ALIVE,会一直保持连接,不受10秒的时间限制。

     */

    socket_tcp1 = tcp_get_socket (TCP_TYPE_CLIENT | TCP_TYPE_KEEP_ALIVE, , , tcp_callback1);

     if(socket_tcp1 != )

     {

         res = tcp_connect (socket_tcp1, _remip, _remport, _localport);

         printf_debug1("TCP Socket创建成功res = %d\r\n", res);

     }

}

RL-TCPnet功能测试

这里专门创建了一个app_tcpnet_lib.c文件用于RL-TCPnet功能的测试。

#include "bsp.h"      

#include <RTL.h>

#include <stdio.h>

#include <Net_Config.h>

/*

*********************************************************************************************************

*                                      用于本文件的调试

*********************************************************************************************************

*/

#if 1

     #define printf_debug printf

#else

     #define printf_debug(...)

#endif

/*

*********************************************************************************************************

*                                      外部变量和函数调用

*********************************************************************************************************

*/

extern uint8_t socket_tcp1;

extern void TcpConnect_Client1(uint8_t *_remip, uint16_t _remport, uint16_t _localport);

extern uint8_t socket_tcp2;

extern void TcpConnect_Client2(uint8_t *_remip, uint16_t _remport, uint16_t _localport);

extern uint8_t socket_tcp3;

extern void TcpConnect_Client3(uint8_t *_remip, uint16_t _remport, uint16_t _localport);

/*

*********************************************************************************************************

*                              宏定义,远程服务器的IP和端口

*********************************************************************************************************

*/

/* 要访问的远程服务器IP和端口配置,也就是电脑端调试助手设置的IP和端口号 */

#define IP1            192

#define IP2            168

#define IP3            1

#define IP4            2

#define PORT_NUM         1001

/* 这个是本地端口 */

#define LocalPort_NUM1    1024

#define LocalPort_NUM2    1025

#define LocalPort_NUM3    1026

/*

*********************************************************************************************************

*                                         变量

*********************************************************************************************************

*/

uint8_t Rem_IP[] = {IP1,IP2,IP3,IP4};

/* TCP状态的返回值 */

const char * ReVal_Table[]=

{

     "0: TCP_STATE_FREE",                                        

     "1: TCP_STATE_CLOSED",

     "2: TCP_STATE_LISTEN",

     "3: TCP_STATE_SYN_REC",

     "4: TCP_STATE_SYN_SENT",                                         

     "5: TCP_STATE_FINW1",

     "6: TCP_STATE_FINW2",

     "7: TCP_STATE_CLOSING",

     "8: TCP_STATE_LAST_ACK",                                         

     "9: TCP_STATE_TWAIT",

     "10: TCP_STATE_CONNECT",

     "11: 连接还未创建",

};

/*

*********************************************************************************************************

*    函 数 名: tcpnet_poll

*    功能说明: 使用TCPnet必须要一直调用的函数

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

void tcpnet_poll(void)

{

     if(bsp_CheckTimer())

     {

         bsp_LedToggle();

         /* 此函数坚决不可以放在中断里面跑 */

         timer_tick ();

     }

     main_TcpNet ();

}

/*

*********************************************************************************************************

*    函 数 名: TCPnetTest

*    功能说明: TCPnet应用

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

void TCPnetTest(void)

{ 

     int32_t iCount;

     uint8_t *sendbuf;

     uint16_t maxlen;

     uint8_t ucKeyCode;

     uint8_t TcpState;

     /* 初始化网络协议栈 */

     init_TcpNet ();

     /* 创建一个周期是100ms的软定时器 */

    bsp_StartAutoTimer(, );

     while ()

     {

          /* TCP轮询 */

         tcpnet_poll();

          /* 按键消息的处理 */

         ucKeyCode = bsp_GetKey();

         if (ucKeyCode != KEY_NONE)

         {

              switch (ucKeyCode)

              {

                   /* K1键按下,TCP客户端1建立连接 */

                   case KEY_DOWN_K1:     

                       TcpState = tcp_get_state(socket_tcp1);

                        /*

                          TCP_STATE_CONNECT是最后一个状态,未创建连接的话,会返回数值255,这里就是专门处理

                          这种状态,将其设置下一个TCP_STATE_CONNECT+1,即我们这里自定义了一个未创建连接的

                          状态。

                       */

                       if(TcpState > TCP_STATE_CONNECT)

                       {

                            TcpState = TCP_STATE_CONNECT + ;

                       }

                       printf_debug("TCP客户端1状态 = %s\r\n", ReVal_Table[TcpState]);

                       if(TcpState != TCP_STATE_CONNECT)

                       {

                            TcpConnect_Client1(Rem_IP, PORT_NUM, LocalPort_NUM1);

                       }

                       break;

                   /* K2键按下,TCP客户端2建立连接 */

                   case KEY_DOWN_K2:     

                       TcpState = tcp_get_state(socket_tcp2);

                        /*

                          TCP_STATE_CONNECT是最后一个状态,未创建连接的话,会返回数值255,这里就是专门处理

                          这种状态,将其设置下一个TCP_STATE_CONNECT+1,即我们这里自定义了一个未创建连接的

                          状态。

                       */

                       if(TcpState > TCP_STATE_CONNECT)

                       {

                            TcpState = TCP_STATE_CONNECT + ;

                       }

                       printf_debug("TCP客户端2状态 = %s\r\n", ReVal_Table[TcpState]);

                       if(TcpState != TCP_STATE_CONNECT)

                       {

                            TcpConnect_Client2(Rem_IP, PORT_NUM, LocalPort_NUM2);

                       }   

                       break;

                   /* K3键按下,TCP客户端3建立连接 */

                   case KEY_DOWN_K3:            

                       TcpState = tcp_get_state(socket_tcp3);

                        /*

                          TCP_STATE_CONNECT是最后一个状态,未创建连接的话,会返回数值255,这里就是专门处理

                          这种状态,将其设置下一个TCP_STATE_CONNECT+1,即我们这里自定义了一个未创建连接的

                          状态。

                       */

                       if(TcpState > TCP_STATE_CONNECT)

                       {

                            TcpState = TCP_STATE_CONNECT + ;

                       }

                       printf_debug("TCP客户端3状态 = %s\r\n", ReVal_Table[TcpState]);

                       if(TcpState != TCP_STATE_CONNECT)

                        {

                            TcpConnect_Client3(Rem_IP, PORT_NUM, LocalPort_NUM3);

                       }   

                       break;

                   /* 摇杆上键按下,TCP客户端1向TCP服务器发送4096字节数据 */

                   case JOY_DOWN_U:

                       TcpState = tcp_get_state(socket_tcp1);

                       printf_debug("TCP客户端1状态 = %s\r\n", ReVal_Table[TcpState]);                   

                       iCount = ;

                       /* 保证已经建立连接了才可以发送数据 */

                       if(TcpState == TCP_STATE_CONNECT)

                       {

                            do

                            {

                                 tcpnet_poll();

                                 if (tcp_check_send (socket_tcp1) == __TRUE)

                                 {

                                     maxlen = tcp_max_dsize (socket_tcp1);

                                     iCount -= maxlen;

                                     if(iCount < )

                                     {

                                          /* 这么计算没问题的 */

                                          maxlen = iCount + maxlen;

                                     }

                                     /* 这里仅初始化了每次所发送数据包的前8个字节 */

                                     sendbuf = tcp_get_buf(maxlen);

                                     sendbuf[] = 'a';

                                     sendbuf[] = 'b';

                                     sendbuf[] = 'c';

                                     sendbuf[] = 'd';

                                     sendbuf[] = 'e';

                                     sendbuf[] = 'f';

                                     sendbuf[] = 'g';

                                     sendbuf[] = 'h';

                                      /* 测试发现只能使用获取的内存 */

                                     tcp_send (socket_tcp1, sendbuf, maxlen);

                                 }

                            }while(iCount > );        

                       }       

                       break;

                   /* 摇杆左键按下,TCP客户端2向TCP服务器发送4096字节数据 */

                   case JOY_DOWN_L:

                       TcpState = tcp_get_state(socket_tcp2);

                       printf_debug("TCP客户端2状态 = %s\r\n", ReVal_Table[TcpState]);                   

                       iCount = ;

                       /* 保证已经建立连接了才可以发送数据 */

                       if(TcpState == TCP_STATE_CONNECT)

                       {

                            do

                            {

                                 tcpnet_poll();

                                 if (tcp_check_send (socket_tcp2) == __TRUE)

                                 {

                                     maxlen = tcp_max_dsize (socket_tcp2);

                                     iCount -= maxlen;

                                     if(iCount < )

                                     {

                                          /* 这么计算没问题的 */

                                          maxlen = iCount + maxlen;

                                     }

                                      /* 这里仅初始化了每次所发送数据包的前8个字节 */

                                     sendbuf = tcp_get_buf(maxlen);

                                     sendbuf[] = 'a';

                                     sendbuf[] = 'b';

                                     sendbuf[] = 'c';

                                     sendbuf[] = 'd';

                                     sendbuf[] = 'e';

                                     sendbuf[] = 'f';

                                     sendbuf[] = 'g';

                                     sendbuf[] = 'h';

                                     /* 测试发现只能使用获取的内存 */

                                     tcp_send (socket_tcp2, sendbuf, maxlen);

                                }

                            }while(iCount > );        

                       }       

                       break;

                   /* 摇杆右键按下,TCP客户端3向TCP服务器发送4096字节数据 */

                   case JOY_DOWN_R:

                       TcpState = tcp_get_state(socket_tcp3);

                       printf_debug("TCP客户端3状态 = %s\r\n", ReVal_Table[TcpState]);                   

                       iCount = ;

                       /* 保证已经建立连接了才可以发送数据 */

                       if(TcpState == TCP_STATE_CONNECT)

                       {

                            do

                            {

                                 tcpnet_poll();

                                 if (tcp_check_send (socket_tcp3) == __TRUE)

                                 {

                                     maxlen = tcp_max_dsize (socket_tcp3);

                                     iCount -= maxlen;

                                     if(iCount < )

                                     {

                                          /* 这么计算没问题的 */

                                          maxlen = iCount + maxlen;

                                     }

                                      /* 这里仅初始化了每次所发送数据包的前8个字节 */

                                     sendbuf = tcp_get_buf(maxlen);

                                     sendbuf[] = 'a';

                                     sendbuf[] = 'b';

                                      sendbuf[] = 'c';

                                     sendbuf[] = 'd';

                                     sendbuf[] = 'e';

                                     sendbuf[] = 'f';

                                     sendbuf[] = 'g';

                                     sendbuf[] = 'h';

                                      /* 测试发现只能使用获取的内存 */

                                     tcp_send (socket_tcp3, sendbuf, maxlen);

                                 }

                            }while(iCount > );        

                       }       

                       break;

                   /* 其他的键值不处理 */

                   default:                    

                       break;

              }

         }

     }

}

15.8 实验例程说明(RTX)

15.8.1 STM32F407开发板实验

配套例子:

V5-1017_RL-TCPnet实验_多个TCP客户端连接(RTX)

实验目的:

  1. 学习RL-TCPnet的多个TCP客户端创建和数据收发。

实验内容:

  1. 强烈推荐将网线接到路由器或者交换机上面测试,因为已经使能了DHCP,可以自动获取IP地址。
  2. 客户端的例子相比服务器的例子稍麻烦些,因为客户端的例子需要用户知道电脑端IP和端口号。并根据实际情况设置IP和端口号的宏定义,这个配置在文件app_tcpnet_lib.c开头,测试的时候板子要连接这个IP和端口(下面是默认配置,一定要根据实际情况重新配置,如果不会配置,看本例程对应的教程即可):

    #define IP1            192

    #define IP2            168

    #define IP3            1

    #define IP4            2

    #define PORT_NUM       1001

  3. 本例程可以创建三个TCP Client,而且使能了局域网域名NetBIOS,用户只需在电脑端ping armfly就可以获得板子的IP地址,三个TCP Client的端口号分别是1024,1025和1026。
  4. 用户可以在电脑端用网络调试软件创建TCP Server跟这三个客户端建立连接。执行下面5--10步的操作时,优先将电脑端的TCP Server建立起来!
  5. 按键K1按下,创建TCP客户端1,并跟电脑端的TCP服务器建立连接。
  6. 按键K2按下,创建TCP客户端2,并跟电脑端的TCP服务器建立连接。
  7. 按键K3按下,创建TCP客户端3,并跟电脑端的TCP服务器建立连接。
  8. 摇杆上键按下,TCP客户端1给电脑端的TCP服务器发送4096字节数据。
  9. 摇杆左键按下,TCP客户端2给电脑端的TCP服务器发送4096字节数据。
  10. 摇杆右键按下,TCP客户端3给电脑端的TCP服务器发送4096字节数据。

实验操作:

详见本章节15.6小节。

配置向导文件设置(Net_Config.c):

详见本章节15.3小节。

调试文件设置(Net_Debug.c):

详见本章节15.4小节。

RTX配置:

RTX配置向导详情如下:

【RL-TCPnet网络教程】第15章 RL-TCPnet之创建多个TCP连接

Task Configuration

(1)Number of concurrent running tasks

允许创建6个任务,实际创建了如下5个任务:

AppTaskUserIF任务 :按键消息处理。

AppTaskLED任务    :LED闪烁。

AppTaskMsgPro任务 :按键检测。

AppTaskTCPMain任务:RL-TCPnet测试任务。

AppTaskStart任务  :启动任务,也是最高优先级任务,这里实现RL-TCPnet的时间基准更新。

(2)Number of tasks with user-provided stack

创建的5个任务都是采用自定义堆栈方式。

(3)Run in privileged mode

设置任务运行在非特权级模式。

RTX任务调试信息:

【RL-TCPnet网络教程】第15章 RL-TCPnet之创建多个TCP连接

程序设计:

任务栈大小分配:

static uint64_t AppTaskUserIFStk[1024/8];   /* 任务栈 */

static uint64_t AppTaskLEDStk[1024/8];      /* 任务栈 */

static uint64_t AppTaskMsgProStk[1024/8];  /* 任务栈 */

static uint64_t AppTaskTCPMainStk[2048/8]; /* 任务栈 */

static uint64_t AppTaskStartStk[1024/8];     /* 任务栈 */

将任务栈定义成uint64_t类型可以保证任务栈是8字节对齐的,8字节对齐的含义就是数组的首地址对8求余等于0。如果不做8字节对齐的话,部分C语言库函数、浮点运算和uint64_t类型数据运算会出问题。

系统栈大小分配:

【RL-TCPnet网络教程】第15章 RL-TCPnet之创建多个TCP连接

RTX初始化:

/*

*********************************************************************************************************

*    函 数 名: main

*    功能说明: 标准c程序入口。

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

int main (void)

{   

     /* 初始化外设 */

     bsp_Init();

     /* 创建启动任务 */

     os_sys_init_user (AppTaskStart,              /* 任务函数 */

                       ,                         /* 任务优先级 */

                       &AppTaskStartStk,          /* 任务栈 */

                       sizeof(AppTaskStartStk));  /* 任务栈大小,单位字节数 */

     while();

}

硬件外设初始化

硬件外设的初始化是在 bsp.c 文件实现:

/*

*********************************************************************************************************

*    函 数 名: bsp_Init

*    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次

*    形    参:无

*    返 回 值: 无

*********************************************************************************************************

*/

void bsp_Init(void)

{

     /*

         由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。

         启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。

         系统时钟缺省配置为168MHz,如果需要更改,可以修改 system_stm32f4xx.c 文件

     */

     /* 优先级分组设置为4,可配置0-15级抢占式优先级,0级子优先级,即不存在子优先级。*/

     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

     bsp_InitDWT();     /* 初始化DWT */

     bsp_InitUart();    /* 初始化串口 */

     bsp_InitKey();    /* 初始化按键变量(必须在 bsp_InitTimer() 之前调用) */

     bsp_InitLed();    /* 初始LED指示灯端口 */

}

RTX任务创建:

/*

*********************************************************************************************************

*    函 数 名: AppTaskCreate

*    功能说明: 创建应用任务

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

static void AppTaskCreate (void)

{

     HandleTaskUserIF = os_tsk_create_user(AppTaskUserIF,             /* 任务函数 */

                                           ,                         /* 任务优先级 */

                                           &AppTaskUserIFStk,         /* 任务栈 */

                                           sizeof(AppTaskUserIFStk)); /* 任务栈大小,单位字节数 */

     HandleTaskLED = os_tsk_create_user(AppTaskLED,              /* 任务函数 */

                                        ,                       /* 任务优先级 */

                                        &AppTaskLEDStk,          /* 任务栈 */

                                        sizeof(AppTaskLEDStk));  /* 任务栈大小,单位字节数 */

     HandleTaskMsgPro = os_tsk_create_user(AppTaskMsgPro,             /* 任务函数 */

                                           ,                         /* 任务优先级 */

                                           &AppTaskMsgProStk,         /* 任务栈 */

                                           sizeof(AppTaskMsgProStk)); /* 任务栈大小,单位字节数 */

    HandleTaskTCPMain = os_tsk_create_user(AppTaskTCPMain,             /* 任务函数 */

                                           ,                         /* 任务优先级 */

                                           &AppTaskTCPMainStk,         /* 任务栈 */

                                           sizeof(AppTaskTCPMainStk)); /* 任务栈大小,单位字节数 */

}

五个RTX任务的实现:

/*

*********************************************************************************************************

*    函 数 名: AppTaskUserIF

*    功能说明: 按键消息处理     

*    形    参: 无

*    返 回 值: 无

*   优 先 级: 1  (数值越小优先级越低,这个跟uCOS相反)

*********************************************************************************************************

*/

__task void AppTaskUserIF(void)

{

     uint8_t ucKeyCode;

    while()

    {

         ucKeyCode = bsp_GetKey();

         if (ucKeyCode != KEY_NONE)

         {

              switch (ucKeyCode)

              {

                   /* K1键按下,直接发送事件标志给任务AppTaskTCPMain,设置bit0 */

                   case KEY_DOWN_K1:

                       printf("K1键按下,直接发送事件标志给任务AppTaskTCPMain,bit0被设置\r\n");

                       os_evt_set (KEY1_BIT0, HandleTaskTCPMain);                      

                       break;  

                   /* K2键按下,直接发送事件标志给任务AppTaskTCPMain,设置bit1 */

                   case KEY_DOWN_K2:

                       printf("K2键按下,直接发送事件标志给任务AppTaskTCPMain,bit1被设置\r\n");

                       os_evt_set (KEY2_BIT1, HandleTaskTCPMain);

                       break;

                   /* K3键按下,直接发送事件标志给任务AppTaskTCPMain,设置bit2 */

                   case KEY_DOWN_K3:

                       printf("K3键按下,直接发送事件标志给任务AppTaskTCPMain,bit2被设置\r\n");

                       os_evt_set (KEY3_BIT2, HandleTaskTCPMain);

                       break;

                   /* 摇杆上键按下,直接发送事件标志给任务AppTaskTCPMain,设置bit3 */

                   case JOY_DOWN_U:

                       printf("摇杆上键按下,直接发送事件标志给任务AppTaskTCPMain,设置bit3\r\n");

                       os_evt_set (JOY_U_BIT3, HandleTaskTCPMain);

                       break;

                   /* 摇杆左键按下,直接发送事件标志给任务AppTaskTCPMain,设置bit4 */

                   case JOY_DOWN_L:

                       printf("摇杆左键按下,直接发送事件标志给任务AppTaskTCPMain,设置bit4\r\n");

                       os_evt_set (JOY_L_BIT4, HandleTaskTCPMain);

                       break;

                   /* 摇杆右键按下,直接发送事件标志给任务AppTaskTCPMain,设置bit5 */

                   case JOY_DOWN_R:

                       printf("摇杆右键按下,直接发送事件标志给任务AppTaskTCPMain,设置bit5\r\n");

                       os_evt_set (JOY_R_BIT5, HandleTaskTCPMain);

                       break;

                   /* 其他的键值不处理 */

                   default:                    

                       break;

              }

         }

         os_dly_wait();

     }

}

/*

*********************************************************************************************************

*    函 数 名: AppTaskLED

*    功能说明: LED闪烁。

*    形    参: 无

*    返 回 值: 无

*   优 先 级: 2 

*********************************************************************************************************

*/

__task void AppTaskLED(void)

{

     const uint16_t usFrequency = ; /* 延迟周期 */

     /* 设置延迟周期 */

     os_itv_set(usFrequency);

    while()

    {

         bsp_LedToggle();

         /* os_itv_wait是绝对延迟,os_dly_wait是相对延迟。*/

         os_itv_wait();

    }

}

/*

*********************************************************************************************************

*    函 数 名: AppTaskMsgPro

*    功能说明: 按键检测

*    形    参: 无

*    返 回 值: 无

*   优 先 级: 3 

*********************************************************************************************************

*/

__task void AppTaskMsgPro(void)

{

    while()

    {

         bsp_KeyScan();

         os_dly_wait();

    }

}

/*

*********************************************************************************************************

*    函 数 名: AppTaskTCPMain

*    功能说明: RL-TCPnet测试任务

*    形    参: 无

*    返 回 值: 无

*   优 先 级: 4 

*********************************************************************************************************

*/

__task void AppTaskTCPMain(void)

{

     while ()

     {

         TCPnetTest();

     }

}

/*

*********************************************************************************************************

*    函 数 名: AppTaskStart

*    功能说明: 启动任务,也是最高优先级任务,这里实现RL-TCPnet的时间基准更新。

*    形    参: 无

*    返 回 值: 无

*   优 先 级: 5 

*********************************************************************************************************

*/

__task void AppTaskStart(void)

{

     /* 初始化RL-TCPnet */

     init_TcpNet ();

     /* 创建任务 */

     AppTaskCreate();

     os_itv_set ();

    while()

    {

         os_itv_wait ();

         /* RL-TCPnet时间基准更新函数 */

         timer_tick ();

    }

}

三个TCP客户端的实现

三个TCP客户端实现都是一样的,我们这里仅以客户端1为例进行说明:

#include "includes.h"          

/*

*********************************************************************************************************

*                                      用于本文件的调试

*********************************************************************************************************

*/

#if 1

     #define printf_debug1 printf

#else

     #define printf_debug1(...)

#endif

/*

*********************************************************************************************************

*                                         变量

*********************************************************************************************************

*/

uint8_t socket_tcp1;

/*

*********************************************************************************************************

*    函 数 名: tcp_callback

*    功能说明: TCP Socket的回调函数

*    形    参: soc  TCP Socket类型

*             evt  事件类型

*             ptr  事件类型是TCP_EVT_DATA,ptr指向的缓冲区记录着接收到的TCP数据,其余事件记录IP地址

*             par  事件类型是TCP_EVT_DATA,记录接收到的数据个数,其余事件记录端口号

*    返 回 值: 主要是TCP_EVT_CONREQ事件使用,返回数值1允许连接,返回数值0禁止连接。

*********************************************************************************************************

*/

static U16 tcp_callback1 (U8 soc, U8 evt, U8 *ptr, U16 par)

{

     char buf[];

     uint16_t i;

     /* 确保是socket_tcp的回调 */

     if (soc != socket_tcp1)

     {

         return ();

     }

     switch (evt)

     {

         /*

              远程客户端连接消息

             1、数组ptr存储远程设备的IP地址,par中存储端口号。

             2、返回数值1允许连接,返回数值0禁止连接。

         */

         case TCP_EVT_CONREQ:

              sprintf(buf, "远程客户端请求连接IP: %d.%d.%d.%d", ptr[], ptr[], ptr[], ptr[]);

              printf_debug1("IP:%s  port:%d\r\n", buf, par);

              return ();

          /* 连接终止 */

         case TCP_EVT_ABORT:

              break;

          /* Socket远程连接已经建立 */

         case TCP_EVT_CONNECT:

              printf_debug1("Socket is connected to remote peer\r\n");

              break;

          /* 连接断开 */

         case TCP_EVT_CLOSE:

              printf_debug1("Connection has been closed\r\n");

              break;

         /* 发送的数据收到远程设备应答 */

         case TCP_EVT_ACK:

              break;

         /* 接收到TCP数据帧,ptr指向数据地址,par记录数据长度,单位字节 */

         case TCP_EVT_DATA:

              printf_debug1("TCP客户端1接收到数据长度 = %d\r\n", par);

              for(i = ; i < par; i++)

              {

                   printf_debug1("ptr[%d] = %d\r\n", i, ptr[i]);

              }

              break;

     }

     return ();

}

/*

*********************************************************************************************************

*    函 数 名: TcpConnect_Client1

*    功能说明: 创建一个TCP Client

*    形    参: _remip     要访问的远程IP地址

*             _remport   远程端口号

*             _localport 本地端口号

*    返 回 值: 无

*********************************************************************************************************

*/

void TcpConnect_Client1(uint8_t *_remip, uint16_t _remport, uint16_t _localport)

{

     uint8_t res;

     /*

        创建TCP Socket并连接,客户端连接服务器后,10秒内无数据通信将断开连接。

        但是由于这里使能了TCP_TYPE_KEEP_ALIVE,会一直保持连接,不受10秒的时间限制。

     */

    socket_tcp1 = tcp_get_socket (TCP_TYPE_CLIENT | TCP_TYPE_KEEP_ALIVE, , , tcp_callback1);

     if(socket_tcp1 != )

     {

         res = tcp_connect (socket_tcp1, _remip, _remport, _localport);

         printf_debug1("TCP Socket创建成功res = %d\r\n", res);

     }

}

RL-TCPnet功能测试

这里专门创建了一个app_tcpnet_lib.c文件用于RL-TCPnet功能的测试。

#include "includes.h" 

/*

*********************************************************************************************************

*                                      用于本文件的调试

*********************************************************************************************************

*/

#if 1

     #define printf_debug printf

#else

     #define printf_debug(...)

#endif

/*

*********************************************************************************************************

*                                      外部变量和函数调用

*********************************************************************************************************

*/

extern uint8_t socket_tcp1;

extern void TcpConnect_Client1(uint8_t *_remip, uint16_t _remport, uint16_t _localport);

extern uint8_t socket_tcp2;

extern void TcpConnect_Client2(uint8_t *_remip, uint16_t _remport, uint16_t _localport);

extern uint8_t socket_tcp3;

extern void TcpConnect_Client3(uint8_t *_remip, uint16_t _remport, uint16_t _localport);

/*

*********************************************************************************************************

*                              宏定义,远程服务器的IP和端口

*********************************************************************************************************

*/

/* 要访问的远程服务器IP和端口配置,也就是电脑端调试助手设置的IP和端口号 */

#define IP1            192

#define IP2            168

#define IP3            1

#define IP4            2

#define PORT_NUM         1001

/* 这个是本地端口 */

#define LocalPort_NUM1    1024

#define LocalPort_NUM2    1025

#define LocalPort_NUM3    1026

/*

*********************************************************************************************************

*                                         变量

*********************************************************************************************************

*/

uint8_t Rem_IP[] = {IP1,IP2,IP3,IP4};

/* TCP状态的返回值 */

const char * ReVal_Table[]=

{

     "0: TCP_STATE_FREE",                                        

     "1: TCP_STATE_CLOSED",

     "2: TCP_STATE_LISTEN",

     "3: TCP_STATE_SYN_REC",

     "4: TCP_STATE_SYN_SENT",                                         

     "5: TCP_STATE_FINW1",

     "6: TCP_STATE_FINW2",

     "7: TCP_STATE_CLOSING",

     "8: TCP_STATE_LAST_ACK",                                         

     "9: TCP_STATE_TWAIT",

     "10: TCP_STATE_CONNECT",

     "11: 连接还未创建",

};

/*

*********************************************************************************************************

*    函 数 名: TCPnetTest

*    功能说明: TCPnet应用

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

void TCPnetTest(void)

{ 

     int32_t iCount;

     uint8_t *sendbuf;

     uint16_t maxlen;

     uint8_t TcpState;

     OS_RESULT xResult;

     const uint16_t usMaxBlockTime = ; /* 延迟周期 */

     while ()

     {

         /* RL-TCPnet处理函数 */

         main_TcpNet();

          /* 按键消息的处理 */

         if(os_evt_wait_or(0xFFFF, usMaxBlockTime) == OS_R_EVT)

         {

              xResult = os_evt_get ();

              switch (xResult)

              {

                   /* 接收到K1键按下,TCP客户端1建立连接 */

                   case KEY1_BIT0:              

                       TcpState = tcp_get_state(socket_tcp1);

                       /*

                          TCP_STATE_CONNECT是最后一个状态,未创建连接的话,会返回数值255,这里就是专门处理

                          这种状态,将其设置下一个TCP_STATE_CONNECT+1,即我们这里自定义了一个未创建连接的

                          状态。

                       */

                       if(TcpState > TCP_STATE_CONNECT)

                       {

                            TcpState = TCP_STATE_CONNECT + ;

                       }

                       printf_debug("TCP客户端1状态 = %s\r\n", ReVal_Table[TcpState]);

                       if(TcpState != TCP_STATE_CONNECT)

                       {

                            TcpConnect_Client1(Rem_IP, PORT_NUM, LocalPort_NUM1);

                       }

                       break;

                   /* 接收到K2键按下,TCP客户端2建立连接 */

                   case KEY2_BIT1:       

                       TcpState = tcp_get_state(socket_tcp2);

                        /*

                          TCP_STATE_CONNECT是最后一个状态,未创建连接的话,会返回数值255,这里就是专门处理

                          这种状态,将其设置下一个TCP_STATE_CONNECT+1,即我们这里自定义了一个未创建连接的

                          状态。

                       */

                       if(TcpState > TCP_STATE_CONNECT)

                       {

                            TcpState = TCP_STATE_CONNECT + ;

                       }

                       printf_debug("TCP客户端2状态 = %s\r\n", ReVal_Table[TcpState]);

                       if(TcpState != TCP_STATE_CONNECT)

                       {

                            TcpConnect_Client2(Rem_IP, PORT_NUM, LocalPort_NUM2);

                       }   

                       break;

                   /* 接收到K3键按下,TCP客户端3建立连接 */

                   case KEY3_BIT2:              

                       TcpState = tcp_get_state(socket_tcp3);

                       /*

                          TCP_STATE_CONNECT是最后一个状态,未创建连接的话,会返回数值255,这里就是专门处理

                          这种状态,将其设置下一个TCP_STATE_CONNECT+1,即我们这里自定义了一个未创建连接的

                          状态。

                       */

                       if(TcpState > TCP_STATE_CONNECT)

                       {

                            TcpState = TCP_STATE_CONNECT + ;

                       }

                       printf_debug("TCP客户端3状态 = %s\r\n", ReVal_Table[TcpState]);

                       if(TcpState != TCP_STATE_CONNECT)

                       {

                            TcpConnect_Client3(Rem_IP, PORT_NUM, LocalPort_NUM3);

                       }   

                       break;

                   /* 接收到摇杆上键按下,TCP客户端1向TCP服务器发送4096字节数据 */

                   case JOY_U_BIT3:             

                       TcpState = tcp_get_state(socket_tcp1);

                       printf_debug("TCP客户端1状态 = %s\r\n", ReVal_Table[TcpState]);                   

                       iCount = ;

                       /* 保证已经建立连接了才可以发送数据 */

                       if(TcpState == TCP_STATE_CONNECT)

                       {

                            do

                            {

                                 main_TcpNet();

                                 if (tcp_check_send (socket_tcp1) == __TRUE)

                                 {

                                     maxlen = tcp_max_dsize (socket_tcp1);

                                     iCount -= maxlen;

                                     if(iCount < )

                                     {

                                          /* 这么计算没问题的 */

                                          maxlen = iCount + maxlen;

                                     }

                                      /* 这里仅初始化了每次所发送数据包的前8个字节 */

                                     sendbuf = tcp_get_buf(maxlen);

                                     sendbuf[] = 'a';

                                     sendbuf[] = 'b';

                                     sendbuf[] = 'c';

                                     sendbuf[] = 'd';

                                     sendbuf[] = 'e';

                                     sendbuf[] = 'f';

                                     sendbuf[] = 'g';

                                     sendbuf[] = 'h';

                                     /* 测试发现只能使用获取的内存 */

                                     tcp_send (socket_tcp1, sendbuf, maxlen);

                                 }

                            }while(iCount > );        

                       }       

                       break;

                   /* 接收到摇杆左键按下,TCP客户端2向TCP服务器发送4096字节数据 */

                   case JOY_L_BIT4:             

                       TcpState = tcp_get_state(socket_tcp2);

                        printf_debug("TCP客户端2状态 = %s\r\n", ReVal_Table[TcpState]);                   

                       iCount = ;

                       /* 保证已经建立连接了才可以发送数据 */

                       if(TcpState == TCP_STATE_CONNECT)

                       {

                            do

                            {

                                 main_TcpNet();

                                 if (tcp_check_send (socket_tcp2) == __TRUE)

                                 {

                                     maxlen = tcp_max_dsize (socket_tcp2);

                                     iCount -= maxlen;

                                     if(iCount < )

                                     {

                                          /* 这么计算没问题的 */

                                          maxlen = iCount + maxlen;

                                     }

                                      /* 这里仅初始化了每次所发送数据包的前8个字节 */

                                     sendbuf = tcp_get_buf(maxlen);

                                     sendbuf[] = 'a';

                                     sendbuf[] = 'b';

                                     sendbuf[] = 'c';

                                     sendbuf[] = 'd';

                                     sendbuf[] = 'e';

                                     sendbuf[] = 'f';

                                     sendbuf[] = 'g';

                                     sendbuf[] = 'h';

                                     /* 测试发现只能使用获取的内存 */

                                     tcp_send (socket_tcp2, sendbuf, maxlen);

                                 }

                            }while(iCount > );        

                       }       

                       break;

                   /* 接收到摇杆右键按下,TCP客户端3向TCP服务器发送4096字节数据 */

                   case JOY_R_BIT5:             

                       TcpState = tcp_get_state(socket_tcp3);

                       printf_debug("TCP客户端3状态 = %s\r\n", ReVal_Table[TcpState]);                   

                       iCount = ;

                       /* 保证已经建立连接了才可以发送数据 */

                       if(TcpState == TCP_STATE_CONNECT)

                       {

                            do

                            {

                                 main_TcpNet();

                                 if (tcp_check_send (socket_tcp3) == __TRUE)

                                 {

                                     maxlen = tcp_max_dsize (socket_tcp3);

                                     iCount -= maxlen;

                                     if(iCount < )

                                     {

                                          /* 这么计算没问题的 */

                                          maxlen = iCount + maxlen;

                                     }

                                      /* 这里仅初始化了每次所发送数据包的前8个字节 */

                                     sendbuf = tcp_get_buf(maxlen);

                                     sendbuf[] = 'a';

                                     sendbuf[] = 'b';

                                     sendbuf[] = 'c';

                                     sendbuf[] = 'd';

                                     sendbuf[] = 'e';

                                     sendbuf[] = 'f';

                                     sendbuf[] = 'g';

                                     sendbuf[] = 'h';

                                      /* 测试发现只能使用获取的内存 */

                                     tcp_send (socket_tcp3, sendbuf, maxlen);

                                 }

                            }while(iCount > );        

                       }       

                       break;

                   /* 其他的键值不处理 */

                   default:                    

                       break;

              }

         }

     }

}

15.8.2 STM32F429开发板实验

配套例子:

V6-1017_RL-TCPnet实验_多个TCP客户端连接(RTX)

实验目的:

  1. 学习RL-TCPnet的多个TCP客户端创建和数据收发。

实验内容:

  1. 强烈推荐将网线接到路由器或者交换机上面测试,因为已经使能了DHCP,可以自动获取IP地址。
  2. 客户端的例子相比服务器的例子稍麻烦些,因为客户端的例子需要用户知道电脑端IP和端口号。并根据实际情况设置IP和端口号的宏定义,这个配置在文件app_tcpnet_lib.c开头,测试的时候板子要连接这个IP和端口(下面是默认配置,一定要根据实际情况重新配置,如果不会配置,看本例程对应的教程即可):

    #define IP1            192

    #define IP2            168

    #define IP3            1

    #define IP4            2

    #define PORT_NUM       1001

  3. 本例程可以创建三个TCP Client,而且使能了局域网域名NetBIOS,用户只需在电脑端ping armfly就可以获得板子的IP地址,三个TCP Client的端口号分别是1024,1025和1026。
  4. 用户可以在电脑端用网络调试软件创建TCP Server跟这三个客户端建立连接。执行下面5--10步的操作时,优先将电脑端的TCP Server建立起来!
  5. 按键K1按下,创建TCP客户端1,并跟电脑端的TCP服务器建立连接。
  6. 按键K2按下,创建TCP客户端2,并跟电脑端的TCP服务器建立连接。
  7. 按键K3按下,创建TCP客户端3,并跟电脑端的TCP服务器建立连接。
  8. 摇杆上键按下,TCP客户端1给电脑端的TCP服务器发送4096字节数据。
  9. 摇杆左键按下,TCP客户端2给电脑端的TCP服务器发送4096字节数据。
  10. 摇杆右键按下,TCP客户端3给电脑端的TCP服务器发送4096字节数据。

实验操作:

详见本章节15.6小节。

配置向导文件设置(Net_Config.c):

详见本章节15.3小节。

调试文件设置(Net_Debug.c):

详见本章节15.4小节。

RTX配置:

RTX配置向导详情如下:

【RL-TCPnet网络教程】第15章 RL-TCPnet之创建多个TCP连接

Task Configuration

(1)Number of concurrent running tasks

允许创建6个任务,实际创建了如下5个任务:

AppTaskUserIF任务   :按键消息处理。

AppTaskLED任务     :LED闪烁。

AppTaskMsgPro任务 :按键检测。

AppTaskTCPMain任务:RL-TCPnet测试任务。

AppTaskStart任务  :启动任务,也是最高优先级任务,这里实现RL-TCPnet的时间基准更新。

(2)Number of tasks with user-provided stack

创建的5个任务都是采用自定义堆栈方式。

(3)Run in privileged mode

设置任务运行在非特权级模式。

RTX任务调试信息:

【RL-TCPnet网络教程】第15章 RL-TCPnet之创建多个TCP连接

程序设计:

任务栈大小分配:

static uint64_t AppTaskUserIFStk[1024/8];   /* 任务栈 */

static uint64_t AppTaskLEDStk[1024/8];      /* 任务栈 */

static uint64_t AppTaskMsgProStk[1024/8];  /* 任务栈 */

static uint64_t AppTaskTCPMainStk[2048/8]; /* 任务栈 */

static uint64_t AppTaskStartStk[1024/8];     /* 任务栈 */

将任务栈定义成uint64_t类型可以保证任务栈是8字节对齐的,8字节对齐的含义就是数组的首地址对8求余等于0。如果不做8字节对齐的话,部分C语言库函数、浮点运算和uint64_t类型数据运算会出问题。

系统栈大小分配:

【RL-TCPnet网络教程】第15章 RL-TCPnet之创建多个TCP连接

RTX初始化:

/*

*********************************************************************************************************

*    函 数 名: main

*    功能说明: 标准c程序入口。

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

int main (void)

{   

     /* 初始化外设 */

     bsp_Init();

     /* 创建启动任务 */

     os_sys_init_user (AppTaskStart,              /* 任务函数 */

                       ,                         /* 任务优先级 */

                       &AppTaskStartStk,          /* 任务栈 */

                       sizeof(AppTaskStartStk));  /* 任务栈大小,单位字节数 */

     while();

}

硬件外设初始化

硬件外设的初始化是在 bsp.c 文件实现:

/*

*********************************************************************************************************

*    函 数 名: bsp_Init

*    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次

*    形    参:无

*    返 回 值: 无

*********************************************************************************************************

*/

void bsp_Init(void)

{

     /*

         由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。

         启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。

         系统时钟缺省配置为168MHz,如果需要更改,可以修改 system_stm32f4xx.c 文件

     */

     /* 优先级分组设置为4,可配置0-15级抢占式优先级,0级子优先级,即不存在子优先级。*/

     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

     SystemCoreClockUpdate();    /* 根据PLL配置更新系统时钟频率变量 SystemCoreClock */

     bsp_InitDWT();      /* 初始化DWT */

     bsp_InitUart();     /* 初始化串口 */

     bsp_InitKey();     /* 初始化按键变量(必须在 bsp_InitTimer() 之前调用) */

     bsp_InitExtIO();    /* FMC总线上扩展了32位输出IO, 操作LED等外设必须初始化 */

     bsp_InitLed();      /* 初始LED指示灯端口 */

}

RTX任务创建:

/*

*********************************************************************************************************

*    函 数 名: AppTaskCreate

*    功能说明: 创建应用任务

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

static void AppTaskCreate (void)

{

     HandleTaskUserIF = os_tsk_create_user(AppTaskUserIF,             /* 任务函数 */

                                           ,                         /* 任务优先级 */

                                           &AppTaskUserIFStk,         /* 任务栈 */

                                           sizeof(AppTaskUserIFStk)); /* 任务栈大小,单位字节数 */

     HandleTaskLED = os_tsk_create_user(AppTaskLED,              /* 任务函数 */

                                        ,                       /* 任务优先级 */

                                        &AppTaskLEDStk,          /* 任务栈 */

                                        sizeof(AppTaskLEDStk));  /* 任务栈大小,单位字节数 */

     HandleTaskMsgPro = os_tsk_create_user(AppTaskMsgPro,             /* 任务函数 */

                                           ,                         /* 任务优先级 */

                                           &AppTaskMsgProStk,         /* 任务栈 */

                                           sizeof(AppTaskMsgProStk)); /* 任务栈大小,单位字节数 */

    HandleTaskTCPMain = os_tsk_create_user(AppTaskTCPMain,             /* 任务函数 */

                                           ,                         /* 任务优先级 */

                                           &AppTaskTCPMainStk,         /* 任务栈 */

                                           sizeof(AppTaskTCPMainStk)); /* 任务栈大小,单位字节数 */

}

五个RTX任务的实现:

/*

*********************************************************************************************************

*    函 数 名: AppTaskUserIF

*    功能说明: 按键消息处理     

*    形    参: 无

*    返 回 值: 无

*   优 先 级: 1  (数值越小优先级越低,这个跟uCOS相反)

*********************************************************************************************************

*/

__task void AppTaskUserIF(void)

{

     uint8_t ucKeyCode;

    while()

    {

         ucKeyCode = bsp_GetKey();

         if (ucKeyCode != KEY_NONE)

         {

              switch (ucKeyCode)

              {

                   /* K1键按下,直接发送事件标志给任务AppTaskTCPMain,设置bit0 */

                   case KEY_DOWN_K1:

                       printf("K1键按下,直接发送事件标志给任务AppTaskTCPMain,bit0被设置\r\n");

                       os_evt_set (KEY1_BIT0, HandleTaskTCPMain);                      

                       break;  

                   /* K2键按下,直接发送事件标志给任务AppTaskTCPMain,设置bit1 */

                   case KEY_DOWN_K2:

                       printf("K2键按下,直接发送事件标志给任务AppTaskTCPMain,bit1被设置\r\n");

                       os_evt_set (KEY2_BIT1, HandleTaskTCPMain);

                       break;

                   /* K3键按下,直接发送事件标志给任务AppTaskTCPMain,设置bit2 */

                   case KEY_DOWN_K3:

                       printf("K3键按下,直接发送事件标志给任务AppTaskTCPMain,bit2被设置\r\n");

                       os_evt_set (KEY3_BIT2, HandleTaskTCPMain);

                       break;

                   /* 摇杆上键按下,直接发送事件标志给任务AppTaskTCPMain,设置bit3 */

                   case JOY_DOWN_U:

                       printf("摇杆上键按下,直接发送事件标志给任务AppTaskTCPMain,设置bit3\r\n");

                       os_evt_set (JOY_U_BIT3, HandleTaskTCPMain);

                       break;

                   /* 摇杆左键按下,直接发送事件标志给任务AppTaskTCPMain,设置bit4 */

                   case JOY_DOWN_L:

                       printf("摇杆左键按下,直接发送事件标志给任务AppTaskTCPMain,设置bit4\r\n");

                       os_evt_set (JOY_L_BIT4, HandleTaskTCPMain);

                       break;

                   /* 摇杆右键按下,直接发送事件标志给任务AppTaskTCPMain,设置bit5 */

                   case JOY_DOWN_R:

                       printf("摇杆右键按下,直接发送事件标志给任务AppTaskTCPMain,设置bit5\r\n");

                       os_evt_set (JOY_R_BIT5, HandleTaskTCPMain);

                       break;

                   /* 其他的键值不处理 */

                   default:                    

                       break;

              }

         }

         os_dly_wait();

     }

}

/*

*********************************************************************************************************

*    函 数 名: AppTaskLED

*    功能说明: LED闪烁。

*    形    参: 无

*    返 回 值: 无

*   优 先 级: 2 

*********************************************************************************************************

*/

__task void AppTaskLED(void)

{

     const uint16_t usFrequency = ; /* 延迟周期 */

     /* 设置延迟周期 */

     os_itv_set(usFrequency);

    while()

    {

         bsp_LedToggle();

         /* os_itv_wait是绝对延迟,os_dly_wait是相对延迟。*/

         os_itv_wait();

    }

}

/*

*********************************************************************************************************

*    函 数 名: AppTaskMsgPro

*    功能说明: 按键检测

*    形    参: 无

*    返 回 值: 无

*   优 先 级: 3 

*********************************************************************************************************

*/

__task void AppTaskMsgPro(void)

{

    while()

    {

         bsp_KeyScan();

         os_dly_wait();

    }

}

/*

*********************************************************************************************************

*    函 数 名: AppTaskTCPMain

*    功能说明: RL-TCPnet测试任务

*    形    参: 无

*    返 回 值: 无

*   优 先 级: 4 

*********************************************************************************************************

*/

__task void AppTaskTCPMain(void)

{

     while ()

     {

         TCPnetTest();

     }

}

/*

*********************************************************************************************************

*    函 数 名: AppTaskStart

*    功能说明: 启动任务,也是最高优先级任务,这里实现RL-TCPnet的时间基准更新。

*    形    参: 无

*    返 回 值: 无

*   优 先 级: 5 

*********************************************************************************************************

*/

__task void AppTaskStart(void)

{

     /* 初始化RL-TCPnet */

     init_TcpNet ();

     /* 创建任务 */

     AppTaskCreate();

     os_itv_set ();

    while()

    {

         os_itv_wait ();

         /* RL-TCPnet时间基准更新函数 */

         timer_tick ();

    }

}

三个TCP客户端的实现

三个TCP客户端实现都是一样的,我们这里仅以客户端1为例进行说明:

#include "includes.h"          

/*

*********************************************************************************************************

*                                      用于本文件的调试

*********************************************************************************************************

*/

#if 1

     #define printf_debug1 printf

#else

     #define printf_debug1(...)

#endif

/*

*********************************************************************************************************

*                                         变量

*********************************************************************************************************

*/

uint8_t socket_tcp1;

/*

*********************************************************************************************************

*    函 数 名: tcp_callback

*    功能说明: TCP Socket的回调函数

*    形    参: soc  TCP Socket类型

*             evt  事件类型

*             ptr  事件类型是TCP_EVT_DATA,ptr指向的缓冲区记录着接收到的TCP数据,其余事件记录IP地址

*             par  事件类型是TCP_EVT_DATA,记录接收到的数据个数,其余事件记录端口号

*    返 回 值: 主要是TCP_EVT_CONREQ事件使用,返回数值1允许连接,返回数值0禁止连接。

*********************************************************************************************************

*/

static U16 tcp_callback1 (U8 soc, U8 evt, U8 *ptr, U16 par)

{

     char buf[];

     uint16_t i;

     /* 确保是socket_tcp的回调 */

     if (soc != socket_tcp1)

     {

         return ();

     }

     switch (evt)

     {

         /*

              远程客户端连接消息

             1、数组ptr存储远程设备的IP地址,par中存储端口号。

             2、返回数值1允许连接,返回数值0禁止连接。

         */

         case TCP_EVT_CONREQ:

              sprintf(buf, "远程客户端请求连接IP: %d.%d.%d.%d", ptr[], ptr[], ptr[], ptr[]);

              printf_debug1("IP:%s  port:%d\r\n", buf, par);

              return ();

          /* 连接终止 */

         case TCP_EVT_ABORT:

              break;

          /* Socket远程连接已经建立 */

         case TCP_EVT_CONNECT:

              printf_debug1("Socket is connected to remote peer\r\n");

              break;

          /* 连接断开 */

         case TCP_EVT_CLOSE:

              printf_debug1("Connection has been closed\r\n");

              break;

         /* 发送的数据收到远程设备应答 */

         case TCP_EVT_ACK:

              break;

         /* 接收到TCP数据帧,ptr指向数据地址,par记录数据长度,单位字节 */

         case TCP_EVT_DATA:

              printf_debug1("TCP客户端1接收到数据长度 = %d\r\n", par);

              for(i = ; i < par; i++)

              {

                   printf_debug1("ptr[%d] = %d\r\n", i, ptr[i]);

              }

              break;

     }

     return ();

}

/*

*********************************************************************************************************

*    函 数 名: TcpConnect_Client1

*    功能说明: 创建一个TCP Client

*    形    参: _remip     要访问的远程IP地址

*             _remport   远程端口号

*             _localport 本地端口号

*    返 回 值: 无

*********************************************************************************************************

*/

void TcpConnect_Client1(uint8_t *_remip, uint16_t _remport, uint16_t _localport)

{

     uint8_t res;

     /*

        创建TCP Socket并连接,客户端连接服务器后,10秒内无数据通信将断开连接。

        但是由于这里使能了TCP_TYPE_KEEP_ALIVE,会一直保持连接,不受10秒的时间限制。

     */

    socket_tcp1 = tcp_get_socket (TCP_TYPE_CLIENT | TCP_TYPE_KEEP_ALIVE, , , tcp_callback1);

     if(socket_tcp1 != )

     {

         res = tcp_connect (socket_tcp1, _remip, _remport, _localport);

         printf_debug1("TCP Socket创建成功res = %d\r\n", res);

     }

}

RL-TCPnet功能测试

这里专门创建了一个app_tcpnet_lib.c文件用于RL-TCPnet功能的测试。

#include "includes.h" 

/*

*********************************************************************************************************

*                                      用于本文件的调试

*********************************************************************************************************

*/

#if 1

     #define printf_debug printf

#else

     #define printf_debug(...)

#endif

/*

*********************************************************************************************************

*                                      外部变量和函数调用

*********************************************************************************************************

*/

extern uint8_t socket_tcp1;

extern void TcpConnect_Client1(uint8_t *_remip, uint16_t _remport, uint16_t _localport);

extern uint8_t socket_tcp2;

extern void TcpConnect_Client2(uint8_t *_remip, uint16_t _remport, uint16_t _localport);

extern uint8_t socket_tcp3;

extern void TcpConnect_Client3(uint8_t *_remip, uint16_t _remport, uint16_t _localport);

/*

*********************************************************************************************************

*                              宏定义,远程服务器的IP和端口

*********************************************************************************************************

*/

/* 要访问的远程服务器IP和端口配置,也就是电脑端调试助手设置的IP和端口号 */

#define IP1            192

#define IP2            168

#define IP3            1

#define IP4            2

#define PORT_NUM         1001

/* 这个是本地端口 */

#define LocalPort_NUM1    1024

#define LocalPort_NUM2    1025

#define LocalPort_NUM3    1026

/*

*********************************************************************************************************

*                                         变量

*********************************************************************************************************

*/

uint8_t Rem_IP[] = {IP1,IP2,IP3,IP4};

/* TCP状态的返回值 */

const char * ReVal_Table[]=

{

     "0: TCP_STATE_FREE",                                        

     "1: TCP_STATE_CLOSED",

     "2: TCP_STATE_LISTEN",

     "3: TCP_STATE_SYN_REC",

     "4: TCP_STATE_SYN_SENT",                                         

     "5: TCP_STATE_FINW1",

     "6: TCP_STATE_FINW2",

     "7: TCP_STATE_CLOSING",

     "8: TCP_STATE_LAST_ACK",                                         

     "9: TCP_STATE_TWAIT",

     "10: TCP_STATE_CONNECT",

     "11: 连接还未创建",

};

/*

*********************************************************************************************************

*    函 数 名: TCPnetTest

*    功能说明: TCPnet应用

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

void TCPnetTest(void)

{ 

     int32_t iCount;

     uint8_t *sendbuf;

     uint16_t maxlen;

     uint8_t TcpState;

     OS_RESULT xResult;

     const uint16_t usMaxBlockTime = ; /* 延迟周期 */

     while ()

     {

         /* RL-TCPnet处理函数 */

         main_TcpNet();

          /* 按键消息的处理 */

         if(os_evt_wait_or(0xFFFF, usMaxBlockTime) == OS_R_EVT)

         {

              xResult = os_evt_get ();

              switch (xResult)

              {

                   /* 接收到K1键按下,TCP客户端1建立连接 */

                   case KEY1_BIT0:              

                       TcpState = tcp_get_state(socket_tcp1);

                       /*

                          TCP_STATE_CONNECT是最后一个状态,未创建连接的话,会返回数值255,这里就是专门处理

                          这种状态,将其设置下一个TCP_STATE_CONNECT+1,即我们这里自定义了一个未创建连接的

                          状态。

                       */

                       if(TcpState > TCP_STATE_CONNECT)

                       {

                            TcpState = TCP_STATE_CONNECT + ;

                       }

                       printf_debug("TCP客户端1状态 = %s\r\n", ReVal_Table[TcpState]);

                       if(TcpState != TCP_STATE_CONNECT)

                       {

                            TcpConnect_Client1(Rem_IP, PORT_NUM, LocalPort_NUM1);

                       }

                       break;

                   /* 接收到K2键按下,TCP客户端2建立连接 */

                   case KEY2_BIT1:       

                       TcpState = tcp_get_state(socket_tcp2);

                        /*

                          TCP_STATE_CONNECT是最后一个状态,未创建连接的话,会返回数值255,这里就是专门处理

                          这种状态,将其设置下一个TCP_STATE_CONNECT+1,即我们这里自定义了一个未创建连接的

                          状态。

                       */

                       if(TcpState > TCP_STATE_CONNECT)

                       {

                            TcpState = TCP_STATE_CONNECT + ;

                       }

                       printf_debug("TCP客户端2状态 = %s\r\n", ReVal_Table[TcpState]);

                       if(TcpState != TCP_STATE_CONNECT)

                       {

                            TcpConnect_Client2(Rem_IP, PORT_NUM, LocalPort_NUM2);

                       }   

                       break;

                   /* 接收到K3键按下,TCP客户端3建立连接 */

                   case KEY3_BIT2:              

                       TcpState = tcp_get_state(socket_tcp3);

                       /*

                          TCP_STATE_CONNECT是最后一个状态,未创建连接的话,会返回数值255,这里就是专门处理

                          这种状态,将其设置下一个TCP_STATE_CONNECT+1,即我们这里自定义了一个未创建连接的

                          状态。

                       */

                       if(TcpState > TCP_STATE_CONNECT)

                       {

                            TcpState = TCP_STATE_CONNECT + ;

                       }

                       printf_debug("TCP客户端3状态 = %s\r\n", ReVal_Table[TcpState]);

                       if(TcpState != TCP_STATE_CONNECT)

                       {

                            TcpConnect_Client3(Rem_IP, PORT_NUM, LocalPort_NUM3);

                       }   

                       break;

                   /* 接收到摇杆上键按下,TCP客户端1向TCP服务器发送4096字节数据 */

                   case JOY_U_BIT3:             

                       TcpState = tcp_get_state(socket_tcp1);

                       printf_debug("TCP客户端1状态 = %s\r\n", ReVal_Table[TcpState]);                   

                       iCount = ;

                       /* 保证已经建立连接了才可以发送数据 */

                       if(TcpState == TCP_STATE_CONNECT)

                       {

                            do

                            {

                                 main_TcpNet();

                                 if (tcp_check_send (socket_tcp1) == __TRUE)

                                 {

                                     maxlen = tcp_max_dsize (socket_tcp1);

                                     iCount -= maxlen;

                                     if(iCount < )

                                     {

                                          /* 这么计算没问题的 */

                                          maxlen = iCount + maxlen;

                                     }

                                      /* 这里仅初始化了每次所发送数据包的前8个字节 */

                                     sendbuf = tcp_get_buf(maxlen);

                                     sendbuf[] = 'a';

                                     sendbuf[] = 'b';

                                     sendbuf[] = 'c';

                                     sendbuf[] = 'd';

                                     sendbuf[] = 'e';

                                     sendbuf[] = 'f';

                                     sendbuf[] = 'g';

                                     sendbuf[] = 'h';

                                     /* 测试发现只能使用获取的内存 */

                                     tcp_send (socket_tcp1, sendbuf, maxlen);

                                 }

                            }while(iCount > );        

                       }       

                       break;

                   /* 接收到摇杆左键按下,TCP客户端2向TCP服务器发送4096字节数据 */

                   case JOY_L_BIT4:             

                       TcpState = tcp_get_state(socket_tcp2);

                       printf_debug("TCP客户端2状态 = %s\r\n", ReVal_Table[TcpState]);                   

                       iCount = ;

                       /* 保证已经建立连接了才可以发送数据 */

                       if(TcpState == TCP_STATE_CONNECT)

                       {

                            do

                            {

                                 main_TcpNet();

                                 if (tcp_check_send (socket_tcp2) == __TRUE)

                                 {

                                     maxlen = tcp_max_dsize (socket_tcp2);

                                     iCount -= maxlen;

                                     if(iCount < )

                                     {

                                          /* 这么计算没问题的 */

                                          maxlen = iCount + maxlen;

                                     }

                                      /* 这里仅初始化了每次所发送数据包的前8个字节 */

                                     sendbuf = tcp_get_buf(maxlen);

                                     sendbuf[] = 'a';

                                     sendbuf[] = 'b';

                                     sendbuf[] = 'c';

                                     sendbuf[] = 'd';

                                     sendbuf[] = 'e';

                                     sendbuf[] = 'f';

                                     sendbuf[] = 'g';

                                     sendbuf[] = 'h';

                                     /* 测试发现只能使用获取的内存 */

                                     tcp_send (socket_tcp2, sendbuf, maxlen);

                                 }

                            }while(iCount > );        

                       }       

                       break;

                   /* 接收到摇杆右键按下,TCP客户端3向TCP服务器发送4096字节数据 */

                   case JOY_R_BIT5:             

                       TcpState = tcp_get_state(socket_tcp3);

                       printf_debug("TCP客户端3状态 = %s\r\n", ReVal_Table[TcpState]);                   

                       iCount = ;

                       /* 保证已经建立连接了才可以发送数据 */

                       if(TcpState == TCP_STATE_CONNECT)

                       {

                            do

                            {

                                 main_TcpNet();

                                 if (tcp_check_send (socket_tcp3) == __TRUE)

                                 {

                                     maxlen = tcp_max_dsize (socket_tcp3);

                                     iCount -= maxlen;

                                     if(iCount < )

                                     {

                                          /* 这么计算没问题的 */

                                          maxlen = iCount + maxlen;

                                     }

                                      /* 这里仅初始化了每次所发送数据包的前8个字节 */

                                     sendbuf = tcp_get_buf(maxlen);

                                     sendbuf[] = 'a';

                                     sendbuf[] = 'b';

                                     sendbuf[] = 'c';

                                     sendbuf[] = 'd';

                                     sendbuf[] = 'e';

                                     sendbuf[] = 'f';

                                     sendbuf[] = 'g';

                                     sendbuf[] = 'h';

                                      /* 测试发现只能使用获取的内存 */

                                     tcp_send (socket_tcp3, sendbuf, maxlen);

                                 }

                            }while(iCount > );        

                       }       

                       break;

                   /* 其他的键值不处理 */

                   default:                    

                       break;

              }

         }

     }

}

15.9 总结

本章节就为大家讲解这么多,希望大家多做测试,再多创建几个客户端进行测试。

上一篇:转载-使用 Feed4JUnit 进行数据与代码分离的 Java 单元测试


下一篇:《Android Studio开发实战 从零基础到App上线》资源下载和内容勘误