【RL-TCPnet網絡教程】第13章 RL-TCPnet之TCP服務器


第13章      RL-TCPnet之TCP服務器

本章節為大家講解RL-TCPnet的TCP服務器實現,學習本章節前,務必要優先學習第12章TCP傳輸控制協議基礎知識。有了這些基礎知識之后,再搞本章節會有事半功倍的效果。

本章教程含STM32F407開發板和STM32F429開發板。

13.1  初學者重要提示

13.2  TCP服務器API函數

13.3  TCP配置說明(Net_Config.c)

13.4  TCP調試說明(Net_Debug.c)

13.5  TCP服務器的實現方法

13.6  網絡調試助手和板子的調試操作步驟

13.7  實驗例程說明(裸機)

13.8  實驗例程說明(RTX)

13.9  總結

 

 

13.1  初學者重要提示

1、學習本章節前,務必保證已經學習了第12章的基礎知識。

2、本章要掌握的函數稍多,可以先學會基本的使用,然后再深入了解這些函數使用時的注意事項,爭取達到熟練使用。

3、socket和監聽的關系:

    (1)創建的一個socket只能創建一個監聽。

    (2)創建的一個socket不能夠監聽多個 。

    (3)創建多個socket可以創建多個監聽。

    (4)創建多個socket可以僅創建一個監聽。

 

13.2  TCP服務器API函數

使用如下12個函數可以實現RL-TCPnet的TCP通信:

(1) tcp_get_socket
(2) tcp_connect
(3) tcp_listen
(4) tcp_close
(5) tcp_abort
(6tcp_release_socket
(7) tcp_get_buf
(8) tcp_max_dsize
(9) tcp_send
(10) tcp_get_state
(11) tcp_check_send
(12) tcp_reset_window

關於這12個函數的講解及其使用方法可以看教程第 3 章 3.4 小節里面說的參考資料 rlarm.chm 文件:

 

這里我們重點的說以下 7個函數,因為本章節配套的例子使用的是這 7 個函數:

(1)tcp_get_socket
(2)tcp_listen
(3)tcp_check_send
(4)tcp_max_dsize
(5)tcp_get_buf
(6)tcp_send
(7)tcp_get_state

關於這些函數注意以下兩點:

1、 這些函數都不支持重入,也就是不支持多任務調用。

2、 TCP接口函數通過TCP Socket做數據傳輸,主要用於數據安全作為首選的場合。TCP Socket發送完數據后會等待應答,任何數據包失敗都會重傳。

 

13.2.1  函數tcp_get_socket

函數原型:

U8 tcp_get_socket (

    U8   type,          /* TCP Socket類型          */

    U8   tos,           /* TCP服務類型             */

    U16  tout,          /* 斷開連接前的空閑溢出時間 */

    U16 (*listener)(    /* 回調函數                */

        U8  socket,     /* Socket句柄              */

        U8  event,      /* TCP事件                 */

        U8* ptr,        /* 記錄接收到的數據或者遠程機器的IP地址    */

        U16 par ));     /* 記錄接收到的數據長度或者遠程機器的端口號 */                     

函數描述:

函數tcp_get_socket用於獲取一個TCP Socket。

(1)第1個參數是TCP Socket的類型。

  (2)第2個參數用於指定服務類型,默認取零即可。

(3)第3個參數用於設置空閑溢出時間,單位秒。Keep alive定時器用於監控TCP連接,如果連接的空閑時間(也就是長時間沒有數據通信)超出了,那么會斷開連接。如果設置了TCP_TYPE_KEEP_ALIVE屬性,會通過發送keep alive數據包來保持連接。

(4)第4個參數是回調函數,用於事件監聽。

    a. 回調函數第1個參數,TCP Socket的句柄,也就是函數tcp_get_socket的返回值。

    b. 回調函數第2個參數,事件類型。

 

    c. 回調函數第3個參數,事件類型是TCP_EVT_DATA,ptr指向的緩沖區記錄着接收到的TCP數據,其余事件記錄IP地址。

    d. 回調函數第4個參數,記錄接收到的數據個數,其余事件記錄端口號。

(5)返回值,如果獲取成功,返回TCP Socket句柄,如果獲取失敗,返回0。

使用這個函數要注意以下問題:

  1. 調用TCP Socket任何其它函數前,務必要調用此函數tcp_get_socket。
  2. 使用函數tcp_get_socket,第四個參數的監聽回調函數務必要設置。
  3. 如果需要長時間連接,需要設置屬性TCP_TYPE_KEEP_ALIVE。

使用舉例:

/*

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

*                                      用於本文件的調試

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

*/

#define PORT_NUM       1001    /* TCP服務器監聽端口號 */

 

 

/*

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

*                                         變量

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

*/

uint8_t socket_tcp;

 

 

/*

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

*    函 數 名: tcp_callback

*    功能說明: TCP Socket的回調函數

*    形    參: soc  TCP Socket類型

*             evt  事件類型

*             ptr  事件類型是TCP_EVT_DATA,ptr指向的緩沖區記錄着接收到的TCP數據,其余事件記錄IP地址

*             par  事件類型是TCP_EVT_DATA,記錄接收到的數據個數,其余事件記錄端口號

*    返 回 值:

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

*/

U16 tcp_callback (U8 soc, U8 evt, U8 *ptr, U16 par)

{

     char buf[50];

     uint16_t i;

    

     /* 確保是socket_tcp的回調 */

     if (soc != socket_tcp)

     {

         return (0);

     }

 

     switch (evt)

     {

         /*

              遠程客戶端連接消息

             1、數組ptr存儲遠程設備的IP地址,par中存儲端口號。

             2、返回數值1允許連接,返回數值0禁止連接。

         */

         case TCP_EVT_CONREQ:

              sprintf(buf, "遠程客戶端請求連接IP: %d.%d.%d.%d", ptr[0], ptr[1], ptr[2], ptr[3]);

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

              return (1);

        

         /* 連接終止 */

         case TCP_EVT_ABORT:

              break;

        

         /* Socket遠程連接已經建立 */

         case TCP_EVT_CONNECT:

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

              break;

        

         /* 連接斷開 */

         case TCP_EVT_CLOSE:

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

              break;

        

         /* 發送的數據收到遠程設備應答 */

         case TCP_EVT_ACK:

              break;

        

         /* 接收到TCP數據幀,ptr指向數據地址,par記錄數據長度,單位字節 */

         case TCP_EVT_DATA:

              printf_debug("Data length = %d\r\n", par);

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

              {

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

              }

              break;

     }

    

     return (0);

}

 

/*

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

*    函 數 名: TCPnetTest

*    功能說明: TCPnet應用

*    形    參: 無

*    返 回 值: 無

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

*/

void TCPnetTest(void)

{ 

     uint8_t res;

 

     /*

        創建TCP Socket並創建監聽,客戶端連接服務器后,10秒內無數據通信將斷開連接。

        但是由於這里使能了TCP_TYPE_KEEP_ALIVE,會一直保持連接,不受10秒的時間限制。

     */

    socket_tcp = tcp_get_socket (TCP_TYPE_SERVER|TCP_TYPE_KEEP_ALIVE, 0, 10, tcp_callback);

     if(socket_tcp != 0)

     {

         res = tcp_listen (socket_tcp, PORT_NUM);

         printf_debug("tcp listen res = %d\r\n", res);

     }

    

     while (1)

     {

         /* 省略 */

     }

}

 

13.2.2 函數tcp_listen

函數原型:

BOOL tcp_listen (

    U8  socket,       /* TCP socket 句柄 */

U16 locport );    /* 監聽的端口號 */                 

函數描述:

函數tcp_listen用於設置TCP服務器的監聽端口。

(1)第1個參數是要設置監聽的TCP Socket句柄。

(2)第2個參數是監聽端口號。

(3)返回值,創建監聽成功返回__TRUE,創建失敗返回__FALSE。

使用這個函數要注意以下問題:

  1. RL-TCPnet服務器類應用,比如Telnet Server,HTTP Server,務必要打開一個TCP Socket用於監聽。
  2. 只有創建的TCP服務器才可以使用此函數,也就是調用函數tcp_get_socket的第一個形參必須得是TCP_TYPE_SERVER 或者 TCP_TYPE_CLIENT_SERVER。

使用舉例:

/*

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

*                                      用於本文件的調試

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

*/

#define PORT_NUM       1001    /* TCP服務器監聽端口號 */

 

 

/*

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

*                                         變量

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

*/

uint8_t socket_tcp;

 

 

/*

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

*    函 數 名: tcp_callback

*    功能說明: TCP Socket的回調函數

*    形    參: soc  TCP Socket類型

*             evt  事件類型

*             ptr  事件類型是TCP_EVT_DATA,ptr指向的緩沖區記錄着接收到的TCP數據,其余事件記錄IP地址

*             par  事件類型是TCP_EVT_DATA,記錄接收到的數據個數,其余事件記錄端口號

*    返 回 值:

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

*/

U16 tcp_callback (U8 soc, U8 evt, U8 *ptr, U16 par)

{

     char buf[50];

     uint16_t i;

    

     /* 確保是socket_tcp的回調 */

     if (soc != socket_tcp)

     {

         return (0);

     }

 

     switch (evt)

     {

         /*

              遠程客戶端連接消息

             1、數組ptr存儲遠程設備的IP地址,par中存儲端口號。

             2、返回數值1允許連接,返回數值0禁止連接。

         */

         case TCP_EVT_CONREQ:

              sprintf(buf, "遠程客戶端請求連接IP: %d.%d.%d.%d", ptr[0], ptr[1], ptr[2], ptr[3]);

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

              return (1);

        

         /* 連接終止 */

         case TCP_EVT_ABORT:

              break;

        

         /* Socket遠程連接已經建立 */

         case TCP_EVT_CONNECT:

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

              break;

        

         /* 連接斷開 */

         case TCP_EVT_CLOSE:

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

              break;

        

         /* 發送的數據收到遠程設備應答 */

         case TCP_EVT_ACK:

              break;

        

         /* 接收到TCP數據幀,ptr指向數據地址,par記錄數據長度,單位字節 */

         case TCP_EVT_DATA:

              printf_debug("Data length = %d\r\n", par);

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

              {

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

              }

              break;

     }

    

     return (0);

}

 

/*

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

*    函 數 名: TCPnetTest

*    功能說明: TCPnet應用

*    形    參: 無

*    返 回 值: 無

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

*/

void TCPnetTest(void)

{ 

     uint8_t res;

 

     /*

        創建TCP Socket並創建監聽,客戶端連接服務器后,10秒內無數據通信將斷開連接。

        但是由於這里使能了TCP_TYPE_KEEP_ALIVE,會一直保持連接,不受10秒的時間限制。

     */

    socket_tcp = tcp_get_socket (TCP_TYPE_SERVER|TCP_TYPE_KEEP_ALIVE, 0, 10, tcp_callback);

     if(socket_tcp != 0)

     {

         res = tcp_listen (socket_tcp, PORT_NUM);

         printf_debug("tcp listen res = %d\r\n", res);

     }

    

     while (1)

     {

         /* 省略 */

     }

}

 

13.2.3 函數tcp_check_send

函數原型:

BOOL tcp_check_send (

           U8 socket );    /* TCP socket 句柄 */               

函數描述:

函數tcp_check_send用於檢測是否可以發送數據。此函數通過檢測TCP連接是否建立以及上次發送的數據是否接收到遠程機器的應答來判斷是否可以發送數據。

(1)第1個參數是TCP Socket句柄。

(2)返回值,可以發送數據,返回__TRUE;不可以發送數據,返回__FALSE。

使用舉例:

/*

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

*                                      用於本文件的調試

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

*/

#define PORT_NUM       1001    /* TCP服務器監聽端口號 */

 

 

/*

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

*                                         變量

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

*/

uint8_t socket_tcp;

 

 

/*

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

*    函 數 名: tcp_callback

*    功能說明: TCP Socket的回調函數

*    形    參: soc  TCP Socket類型

*             evt  事件類型

*             ptr  事件類型是TCP_EVT_DATA,ptr指向的緩沖區記錄着接收到的TCP數據,其余事件記錄IP地址

*             par  事件類型是TCP_EVT_DATA,記錄接收到的數據個數,其余事件記錄端口號

*    返 回 值:

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

*/

U16 tcp_callback (U8 soc, U8 evt, U8 *ptr, U16 par)

{

     char buf[50];

     uint16_t i;

    

     /* 確保是socket_tcp的回調 */

     if (soc != socket_tcp)

     {

         return (0);

     }

 

     switch (evt)

     {

         /*

              遠程客戶端連接消息

             1、數組ptr存儲遠程設備的IP地址,par中存儲端口號。

             2、返回數值1允許連接,返回數值0禁止連接。

         */

         case TCP_EVT_CONREQ:

              sprintf(buf, "遠程客戶端請求連接IP: %d.%d.%d.%d", ptr[0], ptr[1], ptr[2], ptr[3]);

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

              return (1);

        

         /* 連接終止 */

         case TCP_EVT_ABORT:

              break;

        

         /* Socket遠程連接已經建立 */

         case TCP_EVT_CONNECT:

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

              break;

        

         /* 連接斷開 */

         case TCP_EVT_CLOSE:

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

              break;

        

         /* 發送的數據收到遠程設備應答 */

         case TCP_EVT_ACK:

              break;

        

         /* 接收到TCP數據幀,ptr指向數據地址,par記錄數據長度,單位字節 */

         case TCP_EVT_DATA:

              printf_debug("Data length = %d\r\n", par);

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

              {

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

              }

              break;

     }

    

     return (0);

}

 

/*

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

*    函 數 名: TCPnetTest

*    功能說明: TCPnet應用

*    形    參: 無

*    返 回 值: 無

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

*/

void TCPnetTest(void)

{ 

     uint8_t res;

 

     /*

        創建TCP Socket並創建監聽,客戶端連接服務器后,10秒內無數據通信將斷開連接。

        但是由於這里使能了TCP_TYPE_KEEP_ALIVE,會一直保持連接,不受10秒的時間限制。

     */

    socket_tcp = tcp_get_socket (TCP_TYPE_SERVER|TCP_TYPE_KEEP_ALIVE, 0, 10, tcp_callback);

     if(socket_tcp != 0)

     {

         res = tcp_listen (socket_tcp, PORT_NUM);

         printf_debug("tcp listen res = %d\r\n", res);

     }

    

     while (1)

     {

/* RL-TCPnet處理函數 */

         main_TcpNet();

        

         /* 用於網線插拔的處理 */

         tcp_status = TCP_StatusCheck();

        

         /* 按鍵消息的處理 */

         if((os_evt_wait_or(0xFFFF, usMaxBlockTime) == OS_R_EVT)&&(tcp_status == __TRUE))

         {

              xResult = os_evt_get ();

              switch (xResult)

              {

                   /* 接收到K1鍵按下,給遠程TCP客戶端發送8字節數據 */

                   case KEY1_BIT0:              

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       iCount = 8;

                       do

                       {

                            main_TcpNet();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 iCount -= maxlen;

                                

                                 if(iCount < 0)

                                 {

                                     /* 這么計算沒問題的 */

                                     maxlen = iCount + maxlen;

                                 }

                                

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = '1';

                                 sendbuf[1] = '2';

                                 sendbuf[2] = '3';

                                 sendbuf[3] = '4';

                                 sendbuf[4] = '5';

                                 sendbuf[5] = '6';

                                 sendbuf[6] = '7';

                                 sendbuf[7] = '8';

                                

                                 /* 測試發現只能使用獲取的內存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(iCount > 0);

                       break;

                  

                    /* 其他的鍵值不處理 */

                   default:                    

                       break;

              }

         }

     }

}

 

13.2.4 函數tcp_max_dsize

函數原型:

U16 tcp_max_dsize (

    U8 socket );    /* TCP socket 句柄 */                 

函數描述:

函數tcp_max_dsize用於獲得當前可以發送的最大報文長度(MSS,Maximum Segment Size)。在配置向導中,默認配置的MSS是1460字節,然而在實際建立連接后,此值會被動態調整,但一定是小於等於1460字節的。

(1)第1個參數是TCP Socket句柄。

(2)返回值,返回本次可以發送的最大報文長度,單位字節。

使用舉例:

/*

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

*                                      用於本文件的調試

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

*/

#define PORT_NUM       1001    /* TCP服務器監聽端口號 */

 

 

/*

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

*                                         變量

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

*/

uint8_t socket_tcp;

 

 

/*

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

*    函 數 名: tcp_callback

*    功能說明: TCP Socket的回調函數

*    形    參: soc  TCP Socket類型

*             evt  事件類型

*             ptr  事件類型是TCP_EVT_DATA,ptr指向的緩沖區記錄着接收到的TCP數據,其余事件記錄IP地址

*             par  事件類型是TCP_EVT_DATA,記錄接收到的數據個數,其余事件記錄端口號

*    返 回 值:

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

*/

U16 tcp_callback (U8 soc, U8 evt, U8 *ptr, U16 par)

{

     char buf[50];

     uint16_t i;

    

     /* 確保是socket_tcp的回調 */

     if (soc != socket_tcp)

     {

         return (0);

     }

 

     switch (evt)

     {

         /*

              遠程客戶端連接消息

             1、數組ptr存儲遠程設備的IP地址,par中存儲端口號。

             2、返回數值1允許連接,返回數值0禁止連接。

         */

         case TCP_EVT_CONREQ:

              sprintf(buf, "遠程客戶端請求連接IP: %d.%d.%d.%d", ptr[0], ptr[1], ptr[2], ptr[3]);

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

              return (1);

        

         /* 連接終止 */

         case TCP_EVT_ABORT:

              break;

        

         /* Socket遠程連接已經建立 */

         case TCP_EVT_CONNECT:

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

              break;

        

         /* 連接斷開 */

         case TCP_EVT_CLOSE:

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

              break;

        

         /* 發送的數據收到遠程設備應答 */

         case TCP_EVT_ACK:

              break;

        

         /* 接收到TCP數據幀,ptr指向數據地址,par記錄數據長度,單位字節 */

         case TCP_EVT_DATA:

              printf_debug("Data length = %d\r\n", par);

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

              {

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

              }

              break;

     }

    

     return (0);

}

 

/*

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

*    函 數 名: TCPnetTest

*    功能說明: TCPnet應用

*    形    參: 無

*    返 回 值: 無

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

*/

void TCPnetTest(void)

{ 

     uint8_t res;

 

     /*

        創建TCP Socket並創建監聽,客戶端連接服務器后,10秒內無數據通信將斷開連接。

        但是由於這里使能了TCP_TYPE_KEEP_ALIVE,會一直保持連接,不受10秒的時間限制。

     */

    socket_tcp = tcp_get_socket (TCP_TYPE_SERVER|TCP_TYPE_KEEP_ALIVE, 0, 10, tcp_callback);

     if(socket_tcp != 0)

     {

         res = tcp_listen (socket_tcp, PORT_NUM);

         printf_debug("tcp listen res = %d\r\n", res);

     }

    

     while (1)

     {

/* RL-TCPnet處理函數 */

         main_TcpNet();

        

         /* 用於網線插拔的處理 */

         tcp_status = TCP_StatusCheck();

        

         /* 按鍵消息的處理 */

         if((os_evt_wait_or(0xFFFF, usMaxBlockTime) == OS_R_EVT)&&(tcp_status == __TRUE))

         {

              xResult = os_evt_get ();

              switch (xResult)

              {

                   /* 接收到K1鍵按下,給遠程TCP客戶端發送8字節數據 */

                   case KEY1_BIT0:              

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       iCount = 8;

                       do

                       {

                            main_TcpNet();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 iCount -= maxlen;

                                

                                 if(iCount < 0)

                                 {

                                     /* 這么計算沒問題的 */

                                     maxlen = iCount + maxlen;

                                 }

                                

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = '1';

                                 sendbuf[1] = '2';

                                 sendbuf[2] = '3';

                                 sendbuf[3] = '4';

                                 sendbuf[4] = '5';

                                 sendbuf[5] = '6';

                                 sendbuf[6] = '7';

                                 sendbuf[7] = '8';

                                

                                 /* 測試發現只能使用獲取的內存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(iCount > 0);

                       break;

                  

                    /* 其他的鍵值不處理 */

                   default:                    

                       break;

              }

         }

     }

}

 

13.2.5 函數tcp_get_buf

函數原型:

U8* tcp_get_buf (

U16 size );    /* 申請的緩沖區大小 */

函數描述:

函數tcp_get_buf用於獲取TCP發送緩沖區,用戶將要發送的數據存到這個緩沖區中,然后通過函數tcp_send發送。發送完畢后要等待遠程主機的應答,收到應答后,會在函數tcp_send中釋放申請的發送緩沖區。

(1)第1個參數是要申請的緩沖區大小。

(2)返回值,返回獲取的緩沖區地址。

使用這個函數要注意以下問題:

  1. 每次發送都需要調用此函數獲取發送緩沖區地址。
  2. 申請的發送緩沖區大小不可超過最大報文長度(MSS,Maximum Segment Size),即1460字節。
  3. 操作緩沖區的時候,切不可超過申請的緩沖區大小。

使用舉例:

/*

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

*                                      用於本文件的調試

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

*/

#define PORT_NUM       1001    /* TCP服務器監聽端口號 */

 

 

/*

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

*                                         變量

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

*/

uint8_t socket_tcp;

 

 

/*

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

*    函 數 名: tcp_callback

*    功能說明: TCP Socket的回調函數

*    形    參: soc  TCP Socket類型

*             evt  事件類型

*             ptr  事件類型是TCP_EVT_DATA,ptr指向的緩沖區記錄着接收到的TCP數據,其余事件記錄IP地址

*             par  事件類型是TCP_EVT_DATA,記錄接收到的數據個數,其余事件記錄端口號

*    返 回 值:

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

*/

U16 tcp_callback (U8 soc, U8 evt, U8 *ptr, U16 par)

{

     char buf[50];

     uint16_t i;

    

     /* 確保是socket_tcp的回調 */

     if (soc != socket_tcp)

     {

         return (0);

     }

 

     switch (evt)

     {

         /*

              遠程客戶端連接消息

             1、數組ptr存儲遠程設備的IP地址,par中存儲端口號。

             2、返回數值1允許連接,返回數值0禁止連接。

         */

         case TCP_EVT_CONREQ:

              sprintf(buf, "遠程客戶端請求連接IP: %d.%d.%d.%d", ptr[0], ptr[1], ptr[2], ptr[3]);

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

              return (1);

        

         /* 連接終止 */

         case TCP_EVT_ABORT:

              break;

        

         /* Socket遠程連接已經建立 */

         case TCP_EVT_CONNECT:

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

              break;

        

         /* 連接斷開 */

         case TCP_EVT_CLOSE:

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

              break;

        

         /* 發送的數據收到遠程設備應答 */

         case TCP_EVT_ACK:

              break;

        

         /* 接收到TCP數據幀,ptr指向數據地址,par記錄數據長度,單位字節 */

         case TCP_EVT_DATA:

              printf_debug("Data length = %d\r\n", par);

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

              {

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

              }

              break;

     }

    

     return (0);

}

 

/*

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

*    函 數 名: TCPnetTest

*    功能說明: TCPnet應用

*    形    參: 無

*    返 回 值: 無

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

*/

void TCPnetTest(void)

{ 

     uint8_t res;

 

     /*

        創建TCP Socket並創建監聽,客戶端連接服務器后,10秒內無數據通信將斷開連接。

        但是由於這里使能了TCP_TYPE_KEEP_ALIVE,會一直保持連接,不受10秒的時間限制。

     */

    socket_tcp = tcp_get_socket (TCP_TYPE_SERVER|TCP_TYPE_KEEP_ALIVE, 0, 10, tcp_callback);

     if(socket_tcp != 0)

     {

         res = tcp_listen (socket_tcp, PORT_NUM);

         printf_debug("tcp listen res = %d\r\n", res);

     }

    

     while (1)

     {

/* RL-TCPnet處理函數 */

         main_TcpNet();

        

         /* 用於網線插拔的處理 */

         tcp_status = TCP_StatusCheck();

        

         /* 按鍵消息的處理 */

         if((os_evt_wait_or(0xFFFF, usMaxBlockTime) == OS_R_EVT)&&(tcp_status == __TRUE))

         {

              xResult = os_evt_get ();

              switch (xResult)

              {

                   /* 接收到K1鍵按下,給遠程TCP客戶端發送8字節數據 */

                   case KEY1_BIT0:              

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       iCount = 8;

                       do

                       {

                            main_TcpNet();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 iCount -= maxlen;

                                

                                 if(iCount < 0)

                                 {

                                     /* 這么計算沒問題的 */

                                     maxlen = iCount + maxlen;

                                 }

                                

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = '1';

                                 sendbuf[1] = '2';

                                 sendbuf[2] = '3';

                                 sendbuf[3] = '4';

                                 sendbuf[4] = '5';

                                 sendbuf[5] = '6';

                                 sendbuf[6] = '7';

                                 sendbuf[7] = '8';

                                

                                 /* 測試發現只能使用獲取的內存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(iCount > 0);

                       break;

                  

                    /* 其他的鍵值不處理 */

                   default:                    

                       break;

              }

         }

     }

}

 

13.2.6 函數tcp_send

函數原型:

BOOL tcp_send (

    U8  socket,    /* TCP socket 句柄 */

    U8* buf,       /* 數據緩沖區地址 */

    U16 dlen );    /* 要發送的數據個數,單位字節 */

函數描述:

函數tcp_send用於數據包發送。

(1)第1個參數是TCP Socket句柄。

(2)第2個參數是函數tcp_get_buf獲取的緩沖區地址。

(3)第3個參數是發送數據個數,單位字節。

(4)返回值,發送成功返回__TRUE,發送失敗返回__FALSE。

使用這個函數要注意以下問題:

  1. 不管函數tcp_send發送成功還是失敗,都會釋放通過函數tcp_get_buf獲取的緩沖區。
  2. 以下兩種情況不可使用函數tcp_send發送數據包:

    (1)    TCP連接還未建立。

    (2)    發送給遠程機器的數據包還未收到應答。

  3. 調用函數tcp_send前務必要調用函數tcp_get_buf獲得緩沖區。
  4. 申請的發送緩沖區大小不可超過最大報文長度(MSS,Maximum Segment Size),即1460字節。
  5. 操作緩沖區的時候,切不可超過申請的緩沖區大小。

使用舉例:

/*

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

*                                      用於本文件的調試

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

*/

#define PORT_NUM       1001    /* TCP服務器監聽端口號 */

 

 

/*

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

*                                         變量

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

*/

uint8_t socket_tcp;

 

 

/*

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

*    函 數 名: tcp_callback

*    功能說明: TCP Socket的回調函數

*    形    參: soc  TCP Socket類型

*             evt  事件類型

*             ptr  事件類型是TCP_EVT_DATA,ptr指向的緩沖區記錄着接收到的TCP數據,其余事件記錄IP地址

*             par  事件類型是TCP_EVT_DATA,記錄接收到的數據個數,其余事件記錄端口號

*    返 回 值:

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

*/

U16 tcp_callback (U8 soc, U8 evt, U8 *ptr, U16 par)

{

     char buf[50];

     uint16_t i;

    

     /* 確保是socket_tcp的回調 */

     if (soc != socket_tcp)

     {

         return (0);

     }

 

     switch (evt)

     {

         /*

              遠程客戶端連接消息

             1、數組ptr存儲遠程設備的IP地址,par中存儲端口號。

             2、返回數值1允許連接,返回數值0禁止連接。

         */

         case TCP_EVT_CONREQ:

              sprintf(buf, "遠程客戶端請求連接IP: %d.%d.%d.%d", ptr[0], ptr[1], ptr[2], ptr[3]);

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

              return (1);

        

         /* 連接終止 */

         case TCP_EVT_ABORT:

              break;

        

         /* Socket遠程連接已經建立 */

         case TCP_EVT_CONNECT:

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

              break;

        

         /* 連接斷開 */

         case TCP_EVT_CLOSE:

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

              break;

        

         /* 發送的數據收到遠程設備應答 */

         case TCP_EVT_ACK:

              break;

        

         /* 接收到TCP數據幀,ptr指向數據地址,par記錄數據長度,單位字節 */

         case TCP_EVT_DATA:

              printf_debug("Data length = %d\r\n", par);

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

              {

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

              }

              break;

     }

    

     return (0);

}

 

/*

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

*    函 數 名: TCPnetTest

*    功能說明: TCPnet應用

*    形    參: 無

*    返 回 值: 無

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

*/

void TCPnetTest(void)

{ 

     uint8_t res;

 

     /*

        創建TCP Socket並創建監聽,客戶端連接服務器后,10秒內無數據通信將斷開連接。

        但是由於這里使能了TCP_TYPE_KEEP_ALIVE,會一直保持連接,不受10秒的時間限制。

     */

    socket_tcp = tcp_get_socket (TCP_TYPE_SERVER|TCP_TYPE_KEEP_ALIVE, 0, 10, tcp_callback);

     if(socket_tcp != 0)

     {

         res = tcp_listen (socket_tcp, PORT_NUM);

         printf_debug("tcp listen res = %d\r\n", res);

     }

    

     while (1)

     {

/* RL-TCPnet處理函數 */

         main_TcpNet();

        

         /* 用於網線插拔的處理 */

         tcp_status = TCP_StatusCheck();

        

         /* 按鍵消息的處理 */

         if((os_evt_wait_or(0xFFFF, usMaxBlockTime) == OS_R_EVT)&&(tcp_status == __TRUE))

         {

              xResult = os_evt_get ();

              switch (xResult)

              {

                   /* 接收到K1鍵按下,給遠程TCP客戶端發送8字節數據 */

                   case KEY1_BIT0:              

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       iCount = 8;

                       do

                       {

                            main_TcpNet();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 iCount -= maxlen;

                                

                                 if(iCount < 0)

                                 {

                                     /* 這么計算沒問題的 */

                                     maxlen = iCount + maxlen;

                                 }

                                

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = '1';

                                 sendbuf[1] = '2';

                                 sendbuf[2] = '3';

                                 sendbuf[3] = '4';

                                 sendbuf[4] = '5';

                                 sendbuf[5] = '6';

                                 sendbuf[6] = '7';

                                 sendbuf[7] = '8';

                                

                                 /* 測試發現只能使用獲取的內存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(iCount > 0);

                       break;

                  

                    /* 其他的鍵值不處理 */

                   default:                    

                       break;

              }

         }

     }

}

 

13.2.7 函數tcp_get_state

函數原型:

U8 tcp_get_state (

    U8 socket );    /* TCP socket 句柄 */

函數描述:

函數tcp_get_state用於獲取TCP Socket的當前狀態。用戶應用程序可以通過此函數監控TCP Socket的連接、斷開等狀態。最有用的狀態值是TCP_STATE_CLOSED,TCP_STATE_LISTEN和TCP_STATE_CONNECT。

(1)第1個參數是TCP Socket句柄。

(2)返回值,返回以下幾種狀態值:

 

使用舉例:

/*

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

*    函 數 名: TCP_StatusCheck

*    功能說明: 檢測TCP的連接狀態,主要用於網線插拔的判斷

*    形    參: 無

*    返 回 值: __TRUE  連接

*             __FALSE 斷開

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

*/

uint8_t TCP_StatusCheck(void)

{

     uint8_t res;

    

     switch (tcp_get_state(socket_tcp))

     {

         case TCP_STATE_FREE:

         case TCP_STATE_CLOSED:

              res = tcp_listen (socket_tcp, PORT_NUM);

              printf_debug("tcp listen res = %d\r\n", res);

              break;

        

         case TCP_STATE_LISTEN:

              break;

        

         case TCP_STATE_CONNECT:

              return (__TRUE);

             

         default: 

              break;

     }

    

     return (__FALSE);

}

 

13.3 TCP配置說明(Net_Config.c)

(本章節配套例子的配置與本小節的說明相同)

RL-TCPnet的配置工作是通過配置文件Net_Config.c實現。在MDK工程中打開文件Net_Config.c,可以看到下圖所示的工程配置向導:

 

RL-TCPnet要配置的選項非常多,我們這里把幾個主要的配置選項簡單介紹下。

 

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。

 

Ethernet Network Interface

以太網接口配置,勾選了此選項就可以配置了,如果沒有使能DHCP的話,將使用這里配置的固定IP

(1)MAC Address

局域網內可以隨意配置,只要不跟局域網內其它設備的MAC地址沖突即可。

(2)IP Address

IP地址。

(3)Subnet mask

子網掩碼。

(4)Default Gateway

默認網關。

 

Ethernet Network Interface

以太網接口配置,這個配置里面還有如下兩項比較重要的配置需要說明。

(1)NetBIOS Name Service

NetBIOS局域網域名服務,這里打上對勾就使能了。這樣我們就可以通過前面配置的Local Host Name局域網域名進行訪問,而不需要通過IP地址訪問了。

(2)Dynaminc Host Configuration

即DHCP,這里打上對勾就使能了。使能了DHCP后,RL-TCPnet就可以從外接的路由器上獲得動態IP地址。

 

UDP Sockets

UDP Sockets配置,打上對勾就使能了此項功能

(1)Number of UDP Sockets

用於配置可創建的UDP Sockets數量。

范圍1 – 20。

 

TCP Sockets

TCP Sockets配置,打上對勾就使能了此項功能

(1)Number of TCP Sockets

用於配置可創建的TCP Sockets數量。

(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接收窗口大小。

 

13.4 TCP調試說明(Net_Debug.c)

(重要說明,RL-TCPnet的調試是通過串口打印出來的)

RL-TCPnet的調試功能是通過配置文件Net_Debug.c實現。在MDK工程中打開文件Net_Debug.c,可以看到如圖所示的工程配置向導:

 

Print Time Stamp

勾選了此選項的話,打印消息時,前面會附帶時間信息。

其它所有的選項

默認情況下,所有的調試選項都是關閉的,每個選項有三個調試級別可選擇,這里我們以Memory Management Debug為例,點擊下拉列表,可以看到里面有Off,Errors only和Full debug三個調試級別可供選擇,每個調試選項里面都是這三個級別。

 

Off:表示關閉此選項的調試功能。

Errors only:表示僅在此選項出錯時,將其錯誤打印出來。

Full debug:表示此選項的全功能調試。

具體測試,我們這里就不做了,大家可以按照第11章講解的調試方法進行測試。

 

13.5 TCP服務器的實現方法

有了本章節13.3小節的配置后,剩下的問題就是TCP服務器的創建和TCP服務器數據收發的實現。

13.5.1 創建TCP服務器

TCP服務器的創建比較簡單,調用函數tcp_get_socket即可,此函數的使用和注意事項在本章的13.2.1小節有講解:

/*

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

*                                        宏定義

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

*/

#define PORT_NUM       1001    /* TCP服務器監聽端口號 */

 

 

/*

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

*                                         變量

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

*/

uint8_t socket_tcp;

 

/*

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

*    函 數 名: tcp_callback

*    功能說明: TCP Socket的回調函數

*    形    參: soc  TCP Socket類型

*             evt  事件類型

*             ptr  事件類型是TCP_EVT_DATA,ptr指向的緩沖區記錄着接收到的TCP數據,其余事件記錄IP地址

*             par  事件類型是TCP_EVT_DATA,記錄接收到的數據個數,其余事件記錄端口號

*    返 回 值:

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

*/

U16 tcp_callback (U8 soc, U8 evt, U8 *ptr, U16 par)

{

     char buf[50];

     uint16_t i;

    

     /* 確保是socket_tcp的回調 */

     if (soc != socket_tcp)

     {

         return (0);

     }

 

     switch (evt)

     {

         /*

              遠程客戶端連接消息

             1、數組ptr存儲遠程設備的IP地址,par中存儲端口號。

             2、返回數值1允許連接,返回數值0禁止連接。

         */

         case TCP_EVT_CONREQ:

              sprintf(buf, "遠程客戶端請求連接IP: %d.%d.%d.%d", ptr[0], ptr[1], ptr[2], ptr[3]);

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

              return (1);

        

          /* 連接終止 */

         case TCP_EVT_ABORT:

              break;

        

         /* Socket遠程連接已經建立 */

         case TCP_EVT_CONNECT:

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

              break;

        

         /* 連接斷開 */

         case TCP_EVT_CLOSE:

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

              break;

        

         /* 發送的數據收到遠程設備應答 */

         case TCP_EVT_ACK:

              break;

        

         /* 接收到TCP數據幀,ptr指向數據地址,par記錄數據長度,單位字節 */

         case TCP_EVT_DATA:

              printf_debug("Data length = %d\r\n", par);

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

              {

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

              }

              break;

     }

    

     return (0);

}

 

 

/*

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

*    函 數 名: TCPnetTest

*    功能說明: TCPnet應用

*    形    參: 無

*    返 回 值: 無

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

*/

void TCPnetTest(void)

{ 

     uint8_t res;

    

    

     /* 初始化網絡協議棧 */

     init_TcpNet ();

 

     /*

        創建TCP Socket並創建監聽,客戶端連接服務器后,10秒內無數據通信將斷開連接。

        但是由於這里使能了TCP_TYPE_KEEP_ALIVE,會一直保持連接,不受10秒的時間限制。

     */

    socket_tcp = tcp_get_socket (TCP_TYPE_SERVER, 0, 10, tcp_callback);

     if(socket_tcp != 0)

     {

         res = tcp_listen (socket_tcp, PORT_NUM);

         printf_debug("tcp listen res = %d\r\n", res);

     }

 

    /* 省略 */

 

}

 

13.5.2 TCP數據發送

TCP Socket的數據發送一定要注意各個函數調用順序和使用方法,非常重要!否則,數據發送很容易失敗。數據發送所用到函數的使用方法和注意事項在本章節的13.2小節有講解。下面的代碼中對數據發送專門做了處理,支持任意字節大小的數據發送,僅需修改計數變量iCount的初始值即可,初始值是多少,就是發送多少字節。下面的代碼是裸機方式的,測試發送8字節,1024字節和5MB:

/*

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

*    函 數 名: tcpnet_poll

*    功能說明: 使用TCPnet必須要一直調用的函數

*    形    參: 無

*    返 回 值: 無

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

*/

void tcpnet_poll(void)

{

     if(bsp_CheckTimer(0))

     {

         /* 此函數堅決不可以放在中斷里面跑 */

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

     }

    

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

 

}

 

/*

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

*    函 數 名: TCPnetTest

*    功能說明: TCPnet應用

*    形    參: 無

*    返 回 值: 無

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

*/

void TCPnetTest(void)

{ 

     int32_t iCount;

     uint8_t *sendbuf;

     uint8_t tcp_status;

     uint16_t maxlen;

     uint8_t res;

     uint8_t ucKeyCode;

    

    

     /* 初始化網絡協議棧 */

     init_TcpNet ();

 

     /*

        創建TCP Socket並創建監聽,客戶端連接服務器后,10秒內無數據通信將斷開連接。

        但是由於這里使能了TCP_TYPE_KEEP_ALIVE,會一直保持連接,不受10秒的時間限制。

     */

    socket_tcp = tcp_get_socket (TCP_TYPE_SERVER, 0, 10, tcp_callback);

     if(socket_tcp != 0)

     {

         res = tcp_listen (socket_tcp, PORT_NUM);

         printf_debug("tcp listen res = %d\r\n", res);

     }

 

     /* 創建一個周期是100ms的軟定時器 */

    bsp_StartAutoTimer(0, 100);

    

     while (1)

     {

          /* TCP輪詢 */

         tcpnet_poll();

        

         /* 用於網線插拔的處理 */

         tcp_status = TCP_StatusCheck();

        

         /* 按鍵消息的處理 */

         ucKeyCode = bsp_GetKey();

         if ((ucKeyCode != KEY_NONE)&&(tcp_status == __TRUE))

         {

              switch (ucKeyCode)

              {

                   /* K1鍵按下,給遠程TCP客戶端發送8字節數據 */

                   case KEY_DOWN_K1:            

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

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

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

                       {

                            tcpnet_poll(); 

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 iCount -= maxlen;

                                

                                 if(iCount < 0)

                                 {

                                     /* 這么計算沒問題的 */

                                     maxlen = iCount + maxlen;

                                 }

                                

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = '1';

                                 sendbuf[1] = '2';

                                 sendbuf[2] = '3';

                                 sendbuf[3] = '4';

                                 sendbuf[4] = '5';

                                 sendbuf[5] = '6';

                                 sendbuf[6] = '7';

                                 sendbuf[7] = '8';

                                

                                 /* 測試發現只能使用獲取的內存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(iCount > 0);

                       break;

 

                   /* K2鍵按下,給遠程TCP客戶端發送1024字節的數據 */

                   case KEY_DOWN_K2:     

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       iCount = 1024; //--------------(5)

                       do

                       {

                            tcpnet_poll();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 iCount -= maxlen;

                                

                                 if(iCount < 0)

                                 {

                                     /* 這么計算沒問題的 */

                                     maxlen = iCount + maxlen;

                                 }

                                

                                 /* 這里僅初始化了每次所發送數據包的前8個字節 */

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = 'a';

                                 sendbuf[1] = 'b';

                                 sendbuf[2] = 'c';

                                 sendbuf[3] = 'd';

                                 sendbuf[4] = 'e';

                                 sendbuf[5] = 'f';

                                 sendbuf[6] = 'g';

                                 sendbuf[7] = 'h';

                                

                                 /* 測試發現只能使用獲取的內存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(iCount > 0);                      

                       break;

                      

                   /* K3鍵按下,給遠程TCP客戶端發送5MB數據 */

                   case KEY_DOWN_K3:            

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       iCount = 5*1024*1024; //--------------(6)

                       do

                       {

                            tcpnet_poll();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 iCount -= maxlen;

                                

                                 if(iCount < 0)

                                 {

                                     /* 這么計算沒問題的 */

                                     maxlen = iCount + maxlen;

                                 }

                                

                                 /* 這里僅初始化了每次所發送數據包的前8個字節 */

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = 'a';

                                 sendbuf[1] = 'b';

                                 sendbuf[2] = 'c';

                                 sendbuf[3] = 'd';

                                 sendbuf[4] = 'e';

                                 sendbuf[5] = 'f';

                                 sendbuf[6] = 'g';

                                 sendbuf[7] = 'h';

                                

                                 /* 測試發現只能使用獲取的內存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(iCount > 0);

                       break;

                  

                    /* 其他的鍵值不處理 */

                   default:                    

                       break;

              }

         }

     }

}
  1. 函數timer_tick用於實現網絡時間基准,必須要周期性調用,周期大小是由配置向導文件中參數Tick Timer interval決定的。默認情況下,我們都取100ms。
  2. 函數main_TcpNet必須要一直調用着,協議棧的執行,主要靠它。
  3. 通過變量iCount設置要發送的字節數,這里是發送8字節數據。
  4. do while語句中的流程很重要:

    (1)    函數tcp_poll一定要實時調用着。

    (2)    發送前務必要調用函數tcp_check_send查看發送是否就緒。

    (3)    函數tcp_max_dsize,tcp_get_buf和tcp_send務必要依次調用,一個都不能少。

  5. 通過變量iCount設置要發送的字節數,這里是發送1024字節數據。
  6. 通過變量iCount設置要發送的字節數,這里是發送5MB數據。

 

說完了裸機方式,下面說說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 (100);

    

    while(1)

    {

         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 (1)

     {

         TCPnetTest();

     }

}

 

函數TCPnetTest的具體實現如下:

/*

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

*                                      宏定義

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

*/

#define PORT_NUM       1001    /* TCP服務器監聽端口號 */

 

 

/*

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

*                                         變量

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

*/

uint8_t socket_tcp;

 

/*

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

*    函 數 名: TCPnetTest

*    功能說明: TCPnet應用

*    形    參: 無

*    返 回 值: 無

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

*/

void TCPnetTest(void)

{ 

     int32_t iCount;

     uint8_t *sendbuf;

     uint8_t tcp_status;

     uint16_t maxlen;

     uint8_t res;

     OS_RESULT xResult;

     const uint16_t usMaxBlockTime = 2; /* 延遲周期 */

 

     /*

        創建TCP Socket並創建監聽,客戶端連接服務器后,10秒內無數據通信將斷開連接。

        但是由於這里使能了TCP_TYPE_KEEP_ALIVE,會一直保持連接,不受10秒的時間限制。

     */

    socket_tcp = tcp_get_socket (TCP_TYPE_SERVER|TCP_TYPE_KEEP_ALIVE, 0, 10, tcp_callback);

     if(socket_tcp != 0)

     {

         res = tcp_listen (socket_tcp, PORT_NUM);

         printf_debug("tcp listen res = %d\r\n", res);

     }

    

     while (1)

     {

         /* RL-TCPnet處理函數 */

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

        

         /* 用於網線插拔的處理 */

         tcp_status = TCP_StatusCheck();

        

         /* 按鍵消息的處理 */

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

         {

              xResult = os_evt_get ();

              switch (xResult)

              {

                   /* 接收到K1鍵按下,給遠程TCP客戶端發送8字節數據 */

                   case KEY1_BIT0:              

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

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

                       do

                       {

                            main_TcpNet();//--------------(4)

                            if (tcp_check_send (socket_tcp) == __TRUE)  

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 iCount -= maxlen;

                                

                                 if(iCount < 0)

                                 {

                                     /* 這么計算沒問題的 */

                                     maxlen = iCount + maxlen;

                                 }

                                

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = '1';

                                 sendbuf[1] = '2';

                                 sendbuf[2] = '3';

                                 sendbuf[3] = '4';

                                 sendbuf[4] = '5';

                                 sendbuf[5] = '6';

                                 sendbuf[6] = '7';

                                 sendbuf[7] = '8';

                                

                                 /* 測試發現只能使用獲取的內存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(iCount > 0);

                       break;

 

                   /* 接收到K2鍵按下,給遠程TCP客戶端發送1024字節的數據 */

                   case KEY2_BIT1:       

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       iCount = 1024; //--------------(5)

                       do

                       {

                            main_TcpNet();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 iCount -= maxlen;

                                

                                 if(iCount < 0)

                                 {

                                     /* 這么計算沒問題的 */

                                     maxlen = iCount + maxlen;

                                 }

                                

                                 /* 這里僅初始化了每次所發送數據包的前8個字節 */

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = 'a';

                                 sendbuf[1] = 'b';

                                 sendbuf[2] = 'c';

                                 sendbuf[3] = 'd';

                                 sendbuf[4] = 'e';

                                 sendbuf[5] = 'f';

                                 sendbuf[6] = 'g';

                                 sendbuf[7] = 'h';

                               

                                 /* 測試發現只能使用獲取的內存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(iCount > 0);                      

                       break;

                      

                   /* 接收到K3鍵按下,給遠程TCP客戶端發送5MB數據 */

                   case KEY3_BIT2:              

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       iCount = 5*1024*1024; //--------------(6)

                       do

                       {

                            main_TcpNet();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 iCount -= maxlen;

                                

                                 if(iCount < 0)

                                 {

                                     /* 這么計算沒問題的 */

                                     maxlen = iCount + maxlen;

                                 }

                                

                                 /* 這里僅初始化了每次所發送數據包的前8個字節 */

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = 'a';

                                 sendbuf[1] = 'b';

                                 sendbuf[2] = 'c';

                                 sendbuf[3] = 'd';

                                 sendbuf[4] = 'e';

                                 sendbuf[5] = 'f';

                                 sendbuf[6] = 'g';

                                 sendbuf[7] = 'h';

                                

                                 /* 測試發現只能使用獲取的內存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(iCount > 0);

                       break;

                  

                    /* 其他的鍵值不處理 */

                   default:                    

                       break;

              }

         }

     }

}
  1. 函數main_TcpNet必須要一直調用着,協議棧的執行,主要靠它。
  2. 這里使用了事件標志組,溢出時間設置為2毫秒。這樣一方面保證了函數main_TcpNet的周期性執行,另一方面用來等待按鍵發送事件標志消息。
  3. 通過變量iCount設置要發送的字節數,這里是發送8字節數據。
  4. do while語句中的流程很重要:

    (1)    函數main_TcpNet一定要實時調用着。

    (2)    發送前務必要調用函數tcp_check_send查看發送是否就緒。

    (3)    函數tcp_max_dsize,tcp_get_buf和tcp_send務必要依次調用,一個都不能少。

  5. 通過變量iCount設置要發送的字節數,這里是發送1024字節數據。
  6. 通過變量iCount設置要發送的字節數,這里是發送5MB數據。

 

13.5.3 TCP數據接收

TCP數據接收主要是通過函數tcp_get_socket的回調函數實現(裸機,RTX,uCOS-III和FreeRTOS是一樣的):

/*

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

*    函 數 名: tcp_callback

*    功能說明: TCP Socket的回調函數

*    形    參: soc  TCP Socket類型

*             evt  事件類型

*             ptr  事件類型是TCP_EVT_DATA,ptr指向的緩沖區記錄着接收到的TCP數據,其余事件記錄IP地址

*             par  事件類型是TCP_EVT_DATA,記錄接收到的數據個數,其余事件記錄端口號

*    返 回 值:

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

*/

U16 tcp_callback (U8 soc, U8 evt, U8 *ptr, U16 par)

{

     char buf[50];

     uint16_t i;

    

     /* 確保是socket_tcp的回調 */

     if (soc != socket_tcp)

     {

         return (0);

     }

 

     switch (evt)

     {

         /*

              遠程客戶端連接消息

             1、數組ptr存儲遠程設備的IP地址,par中存儲端口號。

             2、返回數值1允許連接,返回數值0禁止連接。

         */

         case TCP_EVT_CONREQ:

              sprintf(buf, "遠程客戶端請求連接IP: %d.%d.%d.%d", ptr[0], ptr[1], ptr[2], ptr[3]);

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

              return (1);

        

         /* 連接終止 */

         case TCP_EVT_ABORT:

              break;

        

         /* Socket遠程連接已經建立 */

         case TCP_EVT_CONNECT:

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

              break;

        

         /* 連接斷開 */

          case TCP_EVT_CLOSE:

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

              break;

        

         /* 發送的數據收到遠程設備應答 */

         case TCP_EVT_ACK:

              break;

        

         /* 接收到TCP數據幀,ptr指向數據地址,par記錄數據長度,單位字節 */

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

              printf_debug("Data length = %d\r\n", par);

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

              {

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

              }

              break;

     }

    

     return (0);

}
  1. TCP服務器的數據接收主要是通過回調函數的TCP_EVT_DATA消息實現,進入消息后,指針變量ptr是接收數據緩沖區首地址,變量par記錄接收到的數據長度,單位字節。

 

13.6 網絡調試助手和板子的調試操作步驟

我們這里使用下面這款調試助手,任何其它網絡調試助手均可,不限制:http://bbs.armfly.com/read.php?tid=1568

 

13.6.1 獲取板子IP地址

首先,強烈推薦將網線接到路由器或者交換機上面測試,因為已經使能了DHCP,可以自動獲取IP地址,而且在前面的配置中使能了局域網域名NetBIOS,用戶只需在電腦端ping armfly就可以獲取板子的IP地址。測試方法如下:

(1)WIN+R組合鍵打開“運行”窗口,輸入cmd。

 

(2)彈出的命令窗口中,輸入ping armfly。

 

(3)輸入ping armfly后,回車。

 

獲得IP地址是192.168.1.5。

 

13.6.2 網絡調試助手創建TCP客戶端

1、打開調試助手,點擊左上角創建連接:

 

2、彈出如下界面,類型選擇TCP,目標IP設置為192.168.1.5,端口號1001,最后點擊創建:

 

特別說明,我們這里直接填局域網域名armfly也是沒有問題的,即下面這樣:

 

3、創建后的界面效果如下:

 

4、點擊連接,連接后的界面效果如下:

 

連接上后,串口軟件也會打印出如下信息(波特率115200,數據位8,奇偶校驗位無,停止位1):

 

13.6.3 TCP服務器發送數據

板子和網絡調試助手建立連接后就可以相互收發數據了。對於發送數據。程序中創建了三種大小的數據發送測試。

(1)K1按鍵按下,發送了8個字符,從1到8。

 

(2)K2按鍵按下,發送1024字節,每次發送數據包的前8個字節設置了字符a到字符h,后面未做設置。

 

(3)K3按鍵按下,發送5*1024*1024 = 5242880字節,即5MB。每次發送數據包的前8個字節設置了字符a到字符h,后面都未做設置。

 

13.6.4 TCP服務器接收數據

TCP服務器接收數據的測試也比較方便,我們這里通過網絡調試助手給板子發送0到9,共10個字符:

 

點擊發送后,可以看到串口軟件打印出接收到的10個字符:

 

字符0對應的ASCII值就是48,其它字符數值依次增加。測試也是沒問題的。

 

13.7 實驗例程說明(裸機)

13.7.1 STM32F407開發板實驗

配套例子:

V5-1008_RL-TCPnet實驗_TCP服務器(裸機)

實驗目的:

  1. 學習RL-TCPnet的TCP服務器創建和數據收發。

實驗內容:

  1. 強烈推薦將網線接到路由器或者交換機上面測試,因為已經使能了DHCP,可以自動獲取IP地址。
  2. 創建了一個TCP Server,而且使能了局域網域名NetBIOS,用戶只需在電腦端ping armfly就可以獲得板子的IP地址,端口號1001。
  3. 用戶可以在電腦端用網絡調試軟件創建TCP Client連接此服務器端。
  4. 按鍵K1按下,發送8字節的數據給TCP Client。
  5. 按鍵K2按下,發送1024字節的數據給TCP Client。
  6. 按鍵K3按下,發送5MB字節的數據給TCP Client。

實驗操作:

詳見本章節13.6小節。

配置向導文件設置(Net_Config.c):

詳見本章節13.3小節。

調試文件設置(Net_Debug.c):

詳見本章節13.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();   /* 初始化滴答定時器 */

}

RL-TCPnet功能測試

這里專門創建了一個app_tcpnet_lib.c文件用於RL-TCPnet功能的測試,主要功能是創建了一個TCP Server。

/*

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

*                                      用於本文件的調試

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

*/

#if 1

     #define printf_debug printf

#else

     #define printf_debug(...)

#endif

 

 

/*

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

*                                      用於本文件的調試

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

*/

#define PORT_NUM       1001    /* TCP服務器監聽端口號 */

 

 

/*

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

*                                         變量

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

*/

uint8_t socket_tcp;

 

 

/*

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

*    函 數 名: tcp_callback

*    功能說明: TCP Socket的回調函數

*    形    參: soc  TCP Socket類型

*             evt  事件類型

*             ptr  事件類型是TCP_EVT_DATA,ptr指向的緩沖區記錄着接收到的TCP數據,其余事件記錄IP地址

*             par  事件類型是TCP_EVT_DATA,記錄接收到的數據個數,其余事件記錄端口號

*    返 回 值:

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

*/

U16 tcp_callback (U8 soc, U8 evt, U8 *ptr, U16 par)

{

     char buf[50];

     uint16_t i;

    

     /* 確保是socket_tcp的回調 */

     if (soc != socket_tcp)

     {

         return (0);

     }

 

     switch (evt)

     {

         /*

              遠程客戶端連接消息

             1、數組ptr存儲遠程設備的IP地址,par中存儲端口號。

             2、返回數值1允許連接,返回數值0禁止連接。

         */

         case TCP_EVT_CONREQ:

              sprintf(buf, "遠程客戶端請求連接IP: %d.%d.%d.%d", ptr[0], ptr[1], ptr[2], ptr[3]);

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

              return (1);

        

         /* 連接終止 */

         case TCP_EVT_ABORT:

              break;

        

         /* Socket遠程連接已經建立 */

         case TCP_EVT_CONNECT:

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

              break;

        

         /* 連接斷開 */

         case TCP_EVT_CLOSE:

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

              break;

        

         /* 發送的數據收到遠程設備應答 */

         case TCP_EVT_ACK:

              break;

        

          /* 接收到TCP數據幀,ptr指向數據地址,par記錄數據長度,單位字節 */

         case TCP_EVT_DATA:

              printf_debug("Data length = %d\r\n", par);

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

              {

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

              }

              break;

     }

    

     return (0);

}

 

/*

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

*    函 數 名: TCP_StatusCheck

*    功能說明: 檢測TCP的連接狀態,主要用於網線插拔的判斷

*    形    參: 無

*    返 回 值: __TRUE  連接

*             __FALSE 斷開

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

*/

uint8_t TCP_StatusCheck(void)

{

     uint8_t res;

    

     switch (tcp_get_state(socket_tcp))

     {

         case TCP_STATE_FREE:

         case TCP_STATE_CLOSED:

              res = tcp_listen (socket_tcp, PORT_NUM);

              printf_debug("tcp listen res = %d\r\n", res);

              break;

        

         case TCP_STATE_LISTEN:

              break;

        

         case TCP_STATE_CONNECT:

              return (__TRUE);

             

         default: 

              break;

     }

    

     return (__FALSE);

}

 

/*

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

*    函 數 名: tcpnet_poll

*    功能說明: 使用TCPnet必須要一直調用的函數

*    形    參: 無

*    返 回 值: 無

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

*/

void tcpnet_poll(void)

{

     if(bsp_CheckTimer(0))

     {

         /* 此函數堅決不可以放在中斷里面跑 */

         timer_tick ();

     }

    

     main_TcpNet ();

}

 

/*

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

*    函 數 名: TCPnetTest

*    功能說明: TCPnet應用

*    形    參: 無

*    返 回 值: 無

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

*/

void TCPnetTest(void)

{ 

     int32_t ulCount;

     uint8_t *sendbuf;

     uint8_t tcp_status;

     uint16_t maxlen;

     uint8_t res;

     uint8_t ucKeyCode;

    

    

     /* 初始化網絡協議棧 */

     init_TcpNet ();

 

     /*

        創建TCP Socket並創建監聽,客戶端連接服務器后,10秒內無數據通信將斷開連接。

        但是由於這里使能了TCP_TYPE_KEEP_ALIVE,會一直保持連接,不受10秒的時間限制。

     */

    socket_tcp = tcp_get_socket (TCP_TYPE_SERVER | TCP_TYPE_KEEP_ALIVE, 0, 10, tcp_callback);

     if(socket_tcp != 0)

     {

         res = tcp_listen (socket_tcp, PORT_NUM);

         printf_debug("tcp listen res = %d\r\n", res);

     }

 

     /* 創建一個周期是100ms的軟定時器 */

    bsp_StartAutoTimer(0, 100);

    

     while (1)

     {

         /* TCP輪詢 */

         tcpnet_poll();

        

          /* 用於網線插拔的處理 */

         tcp_status = TCP_StatusCheck();

        

          /* 按鍵消息的處理 */

         ucKeyCode = bsp_GetKey();

         if ((ucKeyCode != KEY_NONE)&&(tcp_status == __TRUE))

         {

              switch (ucKeyCode)

              {

                   /* K1鍵按下,給遠程TCP客戶端發送8字節數據 */

                   case KEY_DOWN_K1:            

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       ulCount = 8;

                       do

                       {

                            tcpnet_poll();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 ulCount -= maxlen;

                                

                                 if(ulCount < 0)

                                 {

                                     /* 這么計算沒問題的 */

                                     maxlen = ulCount + maxlen;

                                 }

                                

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = '1';

                                 sendbuf[1] = '2';

                                 sendbuf[2] = '3';

                                 sendbuf[3] = '4';

                                 sendbuf[4] = '5';

                                 sendbuf[5] = '6';

                                 sendbuf[6] = '7';

                                 sendbuf[7] = '8';

                                

                                 /* 測試發現只能使用獲取的內存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(ulCount > 0);

                       break;

 

                   /* K2鍵按下,給遠程TCP客戶端發送1024字節的數據 */

                   case KEY_DOWN_K2:     

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       ulCount = 1024;

                       do

                       {

                            tcpnet_poll();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 ulCount -= maxlen;

                                

                                 if(ulCount < 0)

                                 {

                                     /* 這么計算沒問題的 */

                                     maxlen = ulCount + maxlen;

                                 }

                                

                                 /* 這里僅初始化了每次所發送數據包的前8個字節 */

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = 'a';

                                 sendbuf[1] = 'b';

                                 sendbuf[2] = 'c';

                                 sendbuf[3] = 'd';

                                 sendbuf[4] = 'e';

                                 sendbuf[5] = 'f';

                                 sendbuf[6] = 'g';

                                 sendbuf[7] = 'h';

                                

                                 /* 測試發現只能使用獲取的內存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(ulCount > 0);                     

                       break;

                      

                   /* K3鍵按下,給遠程TCP客戶端發送5MB數據 */

                   case KEY_DOWN_K3:            

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                        ulCount = 5*1024*1024;

                       do

                       {

                            tcpnet_poll();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 ulCount -= maxlen;

                                

                                 if(ulCount < 0)

                                 {

                                     /* 這么計算沒問題的 */

                                     maxlen = ulCount + maxlen;

                                 }

                                

                                 /* 這里僅初始化了每次所發送數據包的前8個字節 */

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = 'a';

                                 sendbuf[1] = 'b';

                                 sendbuf[2] = 'c';

                                 sendbuf[3] = 'd';

                                 sendbuf[4] = 'e';

                                 sendbuf[5] = 'f';

                                 sendbuf[6] = 'g';

                                 sendbuf[7] = 'h';

                                

                                 /* 測試發現只能使用獲取的內存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(ulCount > 0);

                       break;

                  

                    /* 其他的鍵值不處理 */

                   default:                     

                       break;

              }

         }

     }

}

  

13.7.2 STM32F429開發板實驗

配套例子:

V6-1008_RL-TCPnet實驗_TCP服務器(裸機)

實驗目的:

  1. 學習RL-TCPnet的TCP服務器創建和數據收發。

實驗內容:

  1. 強烈推薦將網線接到路由器或者交換機上面測試,因為已經使能了DHCP,可以自動獲取IP地址。
  2. 創建了一個TCP Server,而且使能了局域網域名NetBIOS,用戶只需在電腦端ping armfly就可以獲得板子的IP地址,端口號1001。
  3. 用戶可以在電腦端用網絡調試軟件創建TCP Client連接此服務器端。
  4. 按鍵K1按下,發送8字節的數據給TCP Client。
  5. 按鍵K2按下,發送1024字節的數據給TCP Client。
  6. 按鍵K3按下,發送5MB字節的數據給TCP Client。

實驗操作:

詳見本章節13.6小節。

配置向導文件設置(Net_Config.c):

詳見本章節13.3小節。

調試文件設置(Net_Debug.c):

詳見本章節13.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();   /* 初始化滴答定時器 */

}

RL-TCPnet功能測試

這里專門創建了一個app_tcpnet_lib.c文件用於RL-TCPnet功能的測試,主要功能是創建了一個TCP Server。

/*

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

*                                      用於本文件的調試

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

*/

#if 1

     #define printf_debug printf

#else

     #define printf_debug(...)

#endif

 

 

/*

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

*                                      用於本文件的調試

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

*/

#define PORT_NUM       1001    /* TCP服務器監聽端口號 */

 

 

/*

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

*                                         變量

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

*/

uint8_t socket_tcp;

 

 

/*

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

*    函 數 名: tcp_callback

*    功能說明: TCP Socket的回調函數

*    形    參: soc  TCP Socket類型

*             evt  事件類型

*             ptr  事件類型是TCP_EVT_DATA,ptr指向的緩沖區記錄着接收到的TCP數據,其余事件記錄IP地址

*             par  事件類型是TCP_EVT_DATA,記錄接收到的數據個數,其余事件記錄端口號

*    返 回 值:

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

*/

U16 tcp_callback (U8 soc, U8 evt, U8 *ptr, U16 par)

{

     char buf[50];

     uint16_t i;

    

     /* 確保是socket_tcp的回調 */

     if (soc != socket_tcp)

     {

         return (0);

     }

 

     switch (evt)

     {

         /*

              遠程客戶端連接消息

             1、數組ptr存儲遠程設備的IP地址,par中存儲端口號。

             2、返回數值1允許連接,返回數值0禁止連接。

         */

         case TCP_EVT_CONREQ:

              sprintf(buf, "遠程客戶端請求連接IP: %d.%d.%d.%d", ptr[0], ptr[1], ptr[2], ptr[3]);

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

              return (1);

        

         /* 連接終止 */

         case TCP_EVT_ABORT:

              break;

        

         /* Socket遠程連接已經建立 */

         case TCP_EVT_CONNECT:

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

              break;

        

         /* 連接斷開 */

         case TCP_EVT_CLOSE:

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

              break;

        

         /* 發送的數據收到遠程設備應答 */

         case TCP_EVT_ACK:

              break;

        

          /* 接收到TCP數據幀,ptr指向數據地址,par記錄數據長度,單位字節 */

         case TCP_EVT_DATA:

              printf_debug("Data length = %d\r\n", par);

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

              {

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

              }

              break;

     }

    

     return (0);

}

 

/*

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

*    函 數 名: TCP_StatusCheck

*    功能說明: 檢測TCP的連接狀態,主要用於網線插拔的判斷

*    形    參: 無

*    返 回 值: __TRUE  連接

*             __FALSE 斷開

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

*/

uint8_t TCP_StatusCheck(void)

{

     uint8_t res;

    

     switch (tcp_get_state(socket_tcp))

     {

         case TCP_STATE_FREE:

         case TCP_STATE_CLOSED:

              res = tcp_listen (socket_tcp, PORT_NUM);

              printf_debug("tcp listen res = %d\r\n", res);

              break;

        

         case TCP_STATE_LISTEN:

              break;

        

         case TCP_STATE_CONNECT:

              return (__TRUE);

             

         default: 

              break;

     }

    

     return (__FALSE);

}

 

/*

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

*    函 數 名: tcpnet_poll

*    功能說明: 使用TCPnet必須要一直調用的函數

*    形    參: 無

*    返 回 值: 無

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

*/

void tcpnet_poll(void)

{

     if(bsp_CheckTimer(0))

     {

         /* 此函數堅決不可以放在中斷里面跑 */

         timer_tick ();

     }

    

     main_TcpNet ();

}

 

/*

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

*    函 數 名: TCPnetTest

*    功能說明: TCPnet應用

*    形    參: 無

*    返 回 值: 無

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

*/

void TCPnetTest(void)

{ 

     int32_t ulCount;

     uint8_t *sendbuf;

     uint8_t tcp_status;

     uint16_t maxlen;

     uint8_t res;

     uint8_t ucKeyCode;

    

    

     /* 初始化網絡協議棧 */

     init_TcpNet ();

 

     /*

        創建TCP Socket並創建監聽,客戶端連接服務器后,10秒內無數據通信將斷開連接。

        但是由於這里使能了TCP_TYPE_KEEP_ALIVE,會一直保持連接,不受10秒的時間限制。

     */

    socket_tcp = tcp_get_socket (TCP_TYPE_SERVER | TCP_TYPE_KEEP_ALIVE, 0, 10, tcp_callback);

     if(socket_tcp != 0)

     {

          res = tcp_listen (socket_tcp, PORT_NUM);

         printf_debug("tcp listen res = %d\r\n", res);

     }

 

     /* 創建一個周期是100ms的軟定時器 */

    bsp_StartAutoTimer(0, 100);

    

     while (1)

     {

         /* TCP輪詢 */

         tcpnet_poll();

        

          /* 用於網線插拔的處理 */

         tcp_status = TCP_StatusCheck();

        

          /* 按鍵消息的處理 */

         ucKeyCode = bsp_GetKey();

         if ((ucKeyCode != KEY_NONE)&&(tcp_status == __TRUE))

         {

              switch (ucKeyCode)

              {

                   /* K1鍵按下,給遠程TCP客戶端發送8字節數據 */

                   case KEY_DOWN_K1:            

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       ulCount = 8;

                       do

                       {

                            tcpnet_poll();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 ulCount -= maxlen;

                                

                                 if(ulCount < 0)

                                 {

                                     /* 這么計算沒問題的 */

                                     maxlen = ulCount + maxlen;

                                 }

                                

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = '1';

                                 sendbuf[1] = '2';

                                 sendbuf[2] = '3';

                                 sendbuf[3] = '4';

                                 sendbuf[4] = '5';

                                 sendbuf[5] = '6';

                                 sendbuf[6] = '7';

                                 sendbuf[7] = '8';

                                

                                 /* 測試發現只能使用獲取的內存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(ulCount > 0);

                       break;

 

                   /* K2鍵按下,給遠程TCP客戶端發送1024字節的數據 */

                   case KEY_DOWN_K2:     

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       ulCount = 1024;

                       do

                       {

                            tcpnet_poll();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 ulCount -= maxlen;

                                

                                 if(ulCount < 0)

                                 {

                                     /* 這么計算沒問題的 */

                                     maxlen = ulCount + maxlen;

                                 }

                                

                                 /* 這里僅初始化了每次所發送數據包的前8個字節 */

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = 'a';

                                 sendbuf[1] = 'b';

                                 sendbuf[2] = 'c';

                                 sendbuf[3] = 'd';

                                 sendbuf[4] = 'e';

                                 sendbuf[5] = 'f';

                                 sendbuf[6] = 'g';

                                 sendbuf[7] = 'h';

                                

                                 /* 測試發現只能使用獲取的內存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(ulCount > 0);                     

                       break;

                      

                   /* K3鍵按下,給遠程TCP客戶端發送5MB數據 */

                   case KEY_DOWN_K3:            

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       ulCount = 5*1024*1024;

                       do

                       {

                            tcpnet_poll();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 ulCount -= maxlen;

                                

                                 if(ulCount < 0)

                                 {

                                     /* 這么計算沒問題的 */

                                     maxlen = ulCount + maxlen;

                                 }

                                

                                 /* 這里僅初始化了每次所發送數據包的前8個字節 */

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = 'a';

                                 sendbuf[1] = 'b';

                                 sendbuf[2] = 'c';

                                 sendbuf[3] = 'd';

                                 sendbuf[4] = 'e';

                                 sendbuf[5] = 'f';

                                 sendbuf[6] = 'g';

                                 sendbuf[7] = 'h';

                                

                                 /* 測試發現只能使用獲取的內存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(ulCount > 0);

                       break;

                  

                    /* 其他的鍵值不處理 */

                   default:                    

                       break;

              }

         }

     }

}

 

13.8 實驗例程說明(RTX)

13.8.1 STM32F407開發板實驗

配套例子:

V5-1009_RL-TCPnet實驗_TCP服務器(RTX)

實驗目的:

  1. 學習RL-TCPnet的TCP服務器創建和數據收發。

實驗內容:

  1. 強烈推薦將網線接到路由器或者交換機上面測試,因為已經使能了DHCP,可以自動獲取IP地址。
  2. 創建了一個TCP Server,而且使能了局域網域名NetBIOS,用戶只需在電腦端ping armfly就可以獲得板子的IP地址,端口號1001。
  3. 用戶可以在電腦端用網絡調試軟件創建TCP Client連接此服務器端。
  4. 按鍵K1按下,發送8字節的數據給TCP Client。
  5. 按鍵K2按下,發送1024字節的數據給TCP Client。
  6. 按鍵K3按下,發送5MB字節的數據給TCP Client。

實驗操作:

詳見本章節13.6小節。

配置向導文件設置(Net_Config.c):

詳見本章節13.3小節。

調試文件設置(Net_Debug.c):

詳見本章節13.4小節。

RTX配置:

RTX配置向導詳情如下:

 

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任務調試信息:

 

程序設計:

任務棧大小分配:

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類型數據運算會出問題。

系統棧大小分配:

 

RTX初始化:

/*

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

*    函 數 名: main

*    功能說明: 標准c程序入口。

*    形    參: 無

*    返 回 值: 無

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

*/

int main (void)

{   

     /* 初始化外設 */

     bsp_Init();

    

     /* 創建啟動任務 */

     os_sys_init_user (AppTaskStart,              /* 任務函數 */

                       5,                         /* 任務優先級 */

                       &AppTaskStartStk,          /* 任務棧 */

                       sizeof(AppTaskStartStk));  /* 任務棧大小,單位字節數 */

     while(1);

}

硬件外設初始化

硬件外設的初始化是在 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,             /* 任務函數 */

                                           1,                         /* 任務優先級 */

                                           &AppTaskUserIFStk,         /* 任務棧 */

                                           sizeof(AppTaskUserIFStk)); /* 任務棧大小,單位字節數 */

    

     HandleTaskLED = os_tsk_create_user(AppTaskLED,              /* 任務函數 */

                                        2,                       /* 任務優先級 */

                                        &AppTaskLEDStk,          /* 任務棧 */

                                        sizeof(AppTaskLEDStk));  /* 任務棧大小,單位字節數 */

    

     HandleTaskMsgPro = os_tsk_create_user(AppTaskMsgPro,             /* 任務函數 */

                                           3,                         /* 任務優先級 */

                                           &AppTaskMsgProStk,         /* 任務棧 */

                                           sizeof(AppTaskMsgProStk)); /* 任務棧大小,單位字節數 */

    

    HandleTaskTCPMain = os_tsk_create_user(AppTaskTCPMain,             /* 任務函數 */

                                           4,                         /* 任務優先級 */

                                           &AppTaskTCPMainStk,         /* 任務棧 */

                                           sizeof(AppTaskTCPMainStk)); /* 任務棧大小,單位字節數 */

}

五個RTX任務的實現:

/*

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

*    函 數 名: AppTaskUserIF

*    功能說明: 按鍵消息處理     

*    形    參: 無

*    返 回 值: 無

*   優 先 級: 1  (數值越小優先級越低,這個跟uCOS相反)

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

*/

__task void AppTaskUserIF(void)

{

     uint8_t ucKeyCode;

 

    while(1)

    {

         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;

 

                   /* 其他的鍵值不處理 */

                   default:                    

                       break;

              }

         }

        

         os_dly_wait(20);

     }

}

 

/*

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

*    函 數 名: AppTaskLED

*    功能說明: LED閃爍。

*    形    參: 無

*    返 回 值: 無

*   優 先 級: 2 

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

*/

__task void AppTaskLED(void)

{

     const uint16_t usFrequency = 500; /* 延遲周期 */

    

     /* 設置延遲周期 */

     os_itv_set(usFrequency);

    

    while(1)

    {

         bsp_LedToggle(2);

 

         /* os_itv_wait是絕對延遲,os_dly_wait是相對延遲。*/

         os_itv_wait();

    }

}

 

/*

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

*    函 數 名: AppTaskMsgPro

*    功能說明: 按鍵檢測

*    形    參: 無

*    返 回 值: 無

*   優 先 級: 3 

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

*/

__task void AppTaskMsgPro(void)

{

    while(1)

    {

         bsp_KeyScan();

         os_dly_wait(10);

    }

}

 

/*

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

*    函 數 名: AppTaskTCPMain

*    功能說明: RL-TCPnet測試任務

*    形    參: 無

*    返 回 值: 無

*   優 先 級: 4 

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

*/

__task void AppTaskTCPMain(void)

{

     while (1)

     {

         TCPnetTest();

     }

}

 

/*

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

*    函 數 名: AppTaskStart

*    功能說明: 啟動任務,也是最高優先級任務,這里實現RL-TCPnet的時間基准更新。

*    形    參: 無

*    返 回 值: 無

*   優 先 級: 5 

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

*/

__task void AppTaskStart(void)

{

     /* 初始化RL-TCPnet */

     init_TcpNet ();

    

     /* 創建任務 */

     AppTaskCreate();

    

     os_itv_set (100);

    

    while(1)

    {

         os_itv_wait ();

        

         /* RL-TCPnet時間基准更新函數 */

         timer_tick ();

    }

}

RL-TCPnet功能測試

這里專門創建了一個app_tcpnet_lib.c文件用於RL-TCPnet功能的測試,主要功能是創建了一個TCP Server。

#include "includes.h" 

 

 

 

/*

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

*                                      用於本文件的調試

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

*/

#if 1

     #define printf_debug printf

#else

     #define printf_debug(...)

#endif

 

 

/*

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

*                                      用於本文件的調試

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

*/

#define PORT_NUM       1001    /* TCP服務器監聽端口號 */

 

 

/*

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

*                                         變量

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

*/

uint8_t socket_tcp;

 

 

/*

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

*    函 數 名: tcp_callback

*    功能說明: TCP Socket的回調函數

*    形    參: soc  TCP Socket類型

*             evt  事件類型

*             ptr  事件類型是TCP_EVT_DATA,ptr指向的緩沖區記錄着接收到的TCP數據,其余事件記錄IP地址

*             par  事件類型是TCP_EVT_DATA,記錄接收到的數據個數,其余事件記錄端口號

*    返 回 值:

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

*/

U16 tcp_callback (U8 soc, U8 evt, U8 *ptr, U16 par)

{

     char buf[50];

     uint16_t i;

    

     /* 確保是socket_tcp的回調 */

     if (soc != socket_tcp)

     {

         return (0);

     }

 

     switch (evt)

     {

         /*

              遠程客戶端連接消息

             1、數組ptr存儲遠程設備的IP地址,par中存儲端口號。

             2、返回數值1允許連接,返回數值0禁止連接。

         */

         case TCP_EVT_CONREQ:

              sprintf(buf, "遠程客戶端請求連接IP: %d.%d.%d.%d", ptr[0], ptr[1], ptr[2], ptr[3]);

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

              return (1);

        

          /* 連接終止 */

         case TCP_EVT_ABORT:

              break;

        

         /* Socket遠程連接已經建立 */

         case TCP_EVT_CONNECT:

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

              break;

        

         /* 連接斷開 */

         case TCP_EVT_CLOSE:

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

              break;

        

         /* 發送的數據收到遠程設備應答 */

         case TCP_EVT_ACK:

              break;

        

         /* 接收到TCP數據幀,ptr指向數據地址,par記錄數據長度,單位字節 */

         case TCP_EVT_DATA:

              printf_debug("Data length = %d\r\n", par);

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

              {

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

              }

              break;

     }

    

     return (0);

}

 

/*

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

*    函 數 名: TCP_StatusCheck

*    功能說明: 檢測TCP的連接狀態,主要用於網線插拔的判斷

*    形    參: 無

*    返 回 值: __TRUE  連接

*             __FALSE 斷開

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

*/

uint8_t TCP_StatusCheck(void)

{

     uint8_t res;

    

     switch (tcp_get_state(socket_tcp))

     {

         case TCP_STATE_FREE:

         case TCP_STATE_CLOSED:

              res = tcp_listen (socket_tcp, PORT_NUM);

              printf_debug("tcp listen res = %d\r\n", res);

              break;

        

         case TCP_STATE_LISTEN:

              break;

        

         case TCP_STATE_CONNECT:

              return (__TRUE);

             

         default: 

              break;

     }

    

     return (__FALSE);

}

 

/*

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

*    函 數 名: TCPnetTest

*    功能說明: TCPnet應用

*    形    參: 無

*    返 回 值: 無

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

*/

void TCPnetTest(void)

{ 

     int32_t iCount;

     uint8_t *sendbuf;

     uint8_t tcp_status;

     uint16_t maxlen;

     uint8_t res;

     OS_RESULT xResult;

     const uint16_t usMaxBlockTime = 2; /* 延遲周期 */

 

     /*

        創建TCP Socket並創建監聽,客戶端連接服務器后,10秒內無數據通信將斷開連接。

        但是由於這里使能了TCP_TYPE_KEEP_ALIVE,會一直保持連接,不受10秒的時間限制。

     */

    socket_tcp = tcp_get_socket (TCP_TYPE_SERVER|TCP_TYPE_KEEP_ALIVE, 0, 10, tcp_callback);

     if(socket_tcp != 0)

     {

         res = tcp_listen (socket_tcp, PORT_NUM);

         printf_debug("tcp listen res = %d\r\n", res);

     }

    

     while (1)

     {

         /* RL-TCPnet處理函數 */

         main_TcpNet();

        

         /* 用於網線插拔的處理 */

         tcp_status = TCP_StatusCheck();

        

         /* 按鍵消息的處理 */

         if((os_evt_wait_or(0xFFFF, usMaxBlockTime) == OS_R_EVT)&&(tcp_status == __TRUE))

         {

              xResult = os_evt_get ();

              switch (xResult)

              {

                   /* 接收到K1鍵按下,給遠程TCP客戶端發送8字節數據 */

                   case KEY1_BIT0:              

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       iCount = 8;

                       do

                       {

                            main_TcpNet();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 iCount -= maxlen;

                                

                                 if(iCount < 0)

                                 {

                                      /* 這么計算沒問題的 */

                                     maxlen = iCount + maxlen;

                                 }

                                

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = '1';

                                 sendbuf[1] = '2';

                                 sendbuf[2] = '3';

                                 sendbuf[3] = '4';

                                 sendbuf[4] = '5';

                                 sendbuf[5] = '6';

                                 sendbuf[6] = '7';

                                 sendbuf[7] = '8';

                                

                                 /* 測試發現只能使用獲取的內存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(iCount > 0);

                       break;

 

                   /* 接收到K2鍵按下,給遠程TCP客戶端發送1024字節的數據 */

                   case KEY2_BIT1:       

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       iCount = 1024;

                       do

                       {

                            main_TcpNet();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 iCount -= maxlen;

                                

                                 if(iCount < 0)

                                 {

                                      /* 這么計算沒問題的 */

                                     maxlen = iCount + maxlen;

                                 }

                                

                                 /* 這里僅初始化了每次所發送數據包的前8個字節 */

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = 'a';

                                 sendbuf[1] = 'b';

                                 sendbuf[2] = 'c';

                                 sendbuf[3] = 'd';

                                 sendbuf[4] = 'e';

                                 sendbuf[5] = 'f';

                                 sendbuf[6] = 'g';

                                 sendbuf[7] = 'h';

                                

                                 /* 測試發現只能使用獲取的內存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(iCount > 0);                      

                       break;

                      

                   /* 接收到K3鍵按下,給遠程TCP客戶端發送5MB數據 */

                   case KEY3_BIT2:              

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       iCount = 5*1024*1024;

                       do

                       {

                            main_TcpNet();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 iCount -= maxlen;

                                

                                 if(iCount < 0)

                                 {

                                     /* 這么計算沒問題的 */

                                      maxlen = iCount + maxlen;

                                 }

                                

                                 /* 這里僅初始化了每次所發送數據包的前8個字節 */

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = 'a';

                                 sendbuf[1] = 'b';

                                 sendbuf[2] = 'c';

                                 sendbuf[3] = 'd';

                                 sendbuf[4] = 'e';

                                 sendbuf[5] = 'f';

                                 sendbuf[6] = 'g';

                                 sendbuf[7] = 'h';

                                

                                 /* 測試發現只能使用獲取的內存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(iCount > 0);

                       break;

                  

                    /* 其他的鍵值不處理 */

                   default:                    

                       break;

              }

         }

     }

}

 

13.8.2 STM32F429開發板實驗

配套例子:

V6-1009_RL-TCPnet實驗_TCP服務器(RTX)

實驗目的:

  1. 學習RL-TCPnet的TCP服務器創建和數據收發。

實驗內容:

  1. 強烈推薦將網線接到路由器或者交換機上面測試,因為已經使能了DHCP,可以自動獲取IP地址。
  2. 創建了一個TCP Server,而且使能了局域網域名NetBIOS,用戶只需在電腦端ping armfly就可以獲得板子的IP地址,端口號1001。
  3. 用戶可以在電腦端用網絡調試軟件創建TCP Client連接此服務器端。
  4. 按鍵K1按下,發送8字節的數據給TCP Client。
  5. 按鍵K2按下,發送1024字節的數據給TCP Client。
  6. 按鍵K3按下,發送5MB字節的數據給TCP Client。

實驗操作:

詳見本章節13.6小節。

配置向導文件設置(Net_Config.c):

詳見本章節13.3小節。

調試文件設置(Net_Debug.c):

詳見本章節13.4小節。

RTX配置:

RTX配置向導詳情如下:

 

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任務調試信息:

 

程序設計:

任務棧大小分配:

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類型數據運算會出問題。

系統棧大小分配:

 

RTX初始化:

/*

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

*    函 數 名: main

*    功能說明: 標准c程序入口。

*    形    參: 無

*    返 回 值: 無

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

*/

int main (void)

{   

     /* 初始化外設 */

     bsp_Init();

    

     /* 創建啟動任務 */

     os_sys_init_user (AppTaskStart,              /* 任務函數 */

                       5,                         /* 任務優先級 */

                       &AppTaskStartStk,          /* 任務棧 */

                       sizeof(AppTaskStartStk));  /* 任務棧大小,單位字節數 */

     while(1);

}

硬件外設初始化

硬件外設的初始化是在 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,             /* 任務函數 */

                                           1,                         /* 任務優先級 */

                                           &AppTaskUserIFStk,         /* 任務棧 */

                                           sizeof(AppTaskUserIFStk)); /* 任務棧大小,單位字節數 */

    

     HandleTaskLED = os_tsk_create_user(AppTaskLED,              /* 任務函數 */

                                        2,                       /* 任務優先級 */

                                        &AppTaskLEDStk,          /* 任務棧 */

                                        sizeof(AppTaskLEDStk));  /* 任務棧大小,單位字節數 */

    

     HandleTaskMsgPro = os_tsk_create_user(AppTaskMsgPro,             /* 任務函數 */

                                           3,                         /* 任務優先級 */

                                           &AppTaskMsgProStk,         /* 任務棧 */

                                           sizeof(AppTaskMsgProStk)); /* 任務棧大小,單位字節數 */

    

    HandleTaskTCPMain = os_tsk_create_user(AppTaskTCPMain,             /* 任務函數 */

                                           4,                         /* 任務優先級 */

                                           &AppTaskTCPMainStk,         /* 任務棧 */

                                           sizeof(AppTaskTCPMainStk)); /* 任務棧大小,單位字節數 */

}

五個RTX任務的實現:

/*

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

*    函 數 名: AppTaskUserIF

*    功能說明: 按鍵消息處理     

*    形    參: 無

*    返 回 值: 無

*   優 先 級: 1  (數值越小優先級越低,這個跟uCOS相反)

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

*/

__task void AppTaskUserIF(void)

{

     uint8_t ucKeyCode;

 

    while(1)

    {

         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;

 

                   /* 其他的鍵值不處理 */

                   default:                    

                       break;

              }

         }

        

         os_dly_wait(20);

     }

}

 

/*

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

*    函 數 名: AppTaskLED

*    功能說明: LED閃爍。

*    形    參: 無

*    返 回 值: 無

*   優 先 級: 2 

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

*/

__task void AppTaskLED(void)

{

     const uint16_t usFrequency = 500; /* 延遲周期 */

    

     /* 設置延遲周期 */

     os_itv_set(usFrequency);

    

    while(1)

    {

         bsp_LedToggle(2);

 

         /* os_itv_wait是絕對延遲,os_dly_wait是相對延遲。*/

         os_itv_wait();

    }

}

 

/*

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

*    函 數 名: AppTaskMsgPro

*    功能說明: 按鍵檢測

*    形    參: 無

*    返 回 值: 無

*   優 先 級: 3 

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

*/

__task void AppTaskMsgPro(void)

{

    while(1)

    {

         bsp_KeyScan();

         os_dly_wait(10);

    }

}

 

/*

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

*    函 數 名: AppTaskTCPMain

*    功能說明: RL-TCPnet測試任務

*    形    參: 無

*    返 回 值: 無

*   優 先 級: 4 

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

*/

__task void AppTaskTCPMain(void)

{

     while (1)

     {

         TCPnetTest();

     }

}

 

/*

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

*    函 數 名: AppTaskStart

*    功能說明: 啟動任務,也是最高優先級任務,這里實現RL-TCPnet的時間基准更新。

*    形    參: 無

*    返 回 值: 無

*   優 先 級: 5 

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

*/

__task void AppTaskStart(void)

{

     /* 初始化RL-TCPnet */

     init_TcpNet ();

    

     /* 創建任務 */

     AppTaskCreate();

    

     os_itv_set (100);

    

    while(1)

    {

         os_itv_wait ();

        

         /* RL-TCPnet時間基准更新函數 */

          timer_tick ();

    }

}

RL-TCPnet功能測試

這里專門創建了一個app_tcpnet_lib.c文件用於RL-TCPnet功能的測試,主要功能是創建了一個TCP Server。

#include "includes.h" 

 

 

 

/*

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

*                                      用於本文件的調試

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

*/

#if 1

     #define printf_debug printf

#else

     #define printf_debug(...)

#endif

 

 

/*

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

*                                      用於本文件的調試

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

*/

#define PORT_NUM       1001    /* TCP服務器監聽端口號 */

 

 

/*

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

*                                         變量

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

*/

uint8_t socket_tcp;

 

 

/*

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

*    函 數 名: tcp_callback

*    功能說明: TCP Socket的回調函數

*    形    參: soc  TCP Socket類型

*             evt  事件類型

*             ptr  事件類型是TCP_EVT_DATA,ptr指向的緩沖區記錄着接收到的TCP數據,其余事件記錄IP地址

*             par  事件類型是TCP_EVT_DATA,記錄接收到的數據個數,其余事件記錄端口號

*    返 回 值:

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

*/

U16 tcp_callback (U8 soc, U8 evt, U8 *ptr, U16 par)

{

     char buf[50];

     uint16_t i;

    

     /* 確保是socket_tcp的回調 */

     if (soc != socket_tcp)

     {

         return (0);

     }

 

     switch (evt)

     {

         /*

              遠程客戶端連接消息

             1、數組ptr存儲遠程設備的IP地址,par中存儲端口號。

             2、返回數值1允許連接,返回數值0禁止連接。

         */

         case TCP_EVT_CONREQ:

              sprintf(buf, "遠程客戶端請求連接IP: %d.%d.%d.%d", ptr[0], ptr[1], ptr[2], ptr[3]);

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

              return (1);

        

          /* 連接終止 */

         case TCP_EVT_ABORT:

              break;

        

         /* Socket遠程連接已經建立 */

         case TCP_EVT_CONNECT:

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

              break;

        

         /* 連接斷開 */

         case TCP_EVT_CLOSE:

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

              break;

        

         /* 發送的數據收到遠程設備應答 */

         case TCP_EVT_ACK:

              break;

        

         /* 接收到TCP數據幀,ptr指向數據地址,par記錄數據長度,單位字節 */

         case TCP_EVT_DATA:

              printf_debug("Data length = %d\r\n", par);

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

              {

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

              }

              break;

     }

    

     return (0);

}

 

/*

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

*    函 數 名: TCP_StatusCheck

*    功能說明: 檢測TCP的連接狀態,主要用於網線插拔的判斷

*    形    參: 無

*    返 回 值: __TRUE  連接

*             __FALSE 斷開

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

*/

uint8_t TCP_StatusCheck(void)

{

     uint8_t res;

    

     switch (tcp_get_state(socket_tcp))

     {

         case TCP_STATE_FREE:

         case TCP_STATE_CLOSED:

              res = tcp_listen (socket_tcp, PORT_NUM);

              printf_debug("tcp listen res = %d\r\n", res);

              break;

        

         case TCP_STATE_LISTEN:

              break;

        

         case TCP_STATE_CONNECT:

              return (__TRUE);

             

         default: 

              break;

     }

    

     return (__FALSE);

}

 

/*

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

*    函 數 名: TCPnetTest

*    功能說明: TCPnet應用

*    形    參: 無

*    返 回 值: 無

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

*/

void TCPnetTest(void)

{ 

     int32_t iCount;

     uint8_t *sendbuf;

     uint8_t tcp_status;

     uint16_t maxlen;

     uint8_t res;

     OS_RESULT xResult;

     const uint16_t usMaxBlockTime = 2; /* 延遲周期 */

 

     /*

        創建TCP Socket並創建監聽,客戶端連接服務器后,10秒內無數據通信將斷開連接。

        但是由於這里使能了TCP_TYPE_KEEP_ALIVE,會一直保持連接,不受10秒的時間限制。

     */

    socket_tcp = tcp_get_socket (TCP_TYPE_SERVER|TCP_TYPE_KEEP_ALIVE, 0, 10, tcp_callback);

     if(socket_tcp != 0)

     {

         res = tcp_listen (socket_tcp, PORT_NUM);

         printf_debug("tcp listen res = %d\r\n", res);

     }

    

     while (1)

     {

         /* RL-TCPnet處理函數 */

         main_TcpNet();

        

         /* 用於網線插拔的處理 */

         tcp_status = TCP_StatusCheck();

        

         /* 按鍵消息的處理 */

         if((os_evt_wait_or(0xFFFF, usMaxBlockTime) == OS_R_EVT)&&(tcp_status == __TRUE))

         {

              xResult = os_evt_get ();

              switch (xResult)

              {

                   /* 接收到K1鍵按下,給遠程TCP客戶端發送8字節數據 */

                   case KEY1_BIT0:              

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       iCount = 8;

                       do

                       {

                            main_TcpNet();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 iCount -= maxlen;

                                

                                 if(iCount < 0)

                                 {

                                      /* 這么計算沒問題的 */

                                     maxlen = iCount + maxlen;

                                 }

                                

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = '1';

                                 sendbuf[1] = '2';

                                 sendbuf[2] = '3';

                                 sendbuf[3] = '4';

                                 sendbuf[4] = '5';

                                 sendbuf[5] = '6';

                                 sendbuf[6] = '7';

                                 sendbuf[7] = '8';

                                

                                 /* 測試發現只能使用獲取的內存 */

                                tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(iCount > 0);

                       break;

 

                   /* 接收到K2鍵按下,給遠程TCP客戶端發送1024字節的數據 */

                   case KEY2_BIT1:       

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       iCount = 1024;

                       do

                       {

                            main_TcpNet();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 iCount -= maxlen;

                                

                                 if(iCount < 0)

                                 {

                                      /* 這么計算沒問題的 */

                                     maxlen = iCount + maxlen;

                                 }

                                

                                 /* 這里僅初始化了每次所發送數據包的前8個字節 */

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = 'a';

                                 sendbuf[1] = 'b';

                                 sendbuf[2] = 'c';

                                 sendbuf[3] = 'd';

                                 sendbuf[4] = 'e';

                                 sendbuf[5] = 'f';

                                 sendbuf[6] = 'g';

                                 sendbuf[7] = 'h';

                                

                                 /* 測試發現只能使用獲取的內存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(iCount > 0);                      

                       break;

                      

                   /* 接收到K3鍵按下,給遠程TCP客戶端發送5MB數據 */

                   case KEY3_BIT2:              

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       iCount = 5*1024*1024;

                       do

                       {

                            main_TcpNet();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 iCount -= maxlen;

                                

                                 if(iCount < 0)

                                 {

                                     /* 這么計算沒問題的 */

                                      maxlen = iCount + maxlen;

                                 }

                                

                                 /* 這里僅初始化了每次所發送數據包的前8個字節 */

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = 'a';

                                 sendbuf[1] = 'b';

                                 sendbuf[2] = 'c';

                                 sendbuf[3] = 'd';

                                 sendbuf[4] = 'e';

                                 sendbuf[5] = 'f';

                                 sendbuf[6] = 'g';

                                 sendbuf[7] = 'h';

                                

                                 /* 測試發現只能使用獲取的內存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(iCount > 0);

                       break;

                  

                    /* 其他的鍵值不處理 */

                   default:                    

                       break;

              }

         }

     }

}

 

13.9 總結

本章節就為大家講解這么多,希望大家多做測試,爭取可以熟練掌握這些API函數的使用。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM