1. 程式人生 > 其它 >【STM32F407】第9章 ThreadX NetXDUO之TCP客戶端

【STM32F407】第9章 ThreadX NetXDUO之TCP客戶端

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

第9章 ThreadX NetXDUO之TCP客戶端

本章節為大家講解NetXDUO的TCP客戶端實現,學習本章節前,務必要優先學習第7章TCP傳輸控制協議基礎知識。有了這些基礎知識之後,再搞本章節會有事半功倍的效果。

9.1 初學者重要提示

9.2 TCP客戶端API函式

9.3 TCP客戶端的實現方法

9.4 網路除錯助手和板子的除錯操作步驟

9.5 實驗例程說明

9.6 總結

9.1 初學者重要提示

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

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

3、socket和監聽的關係:

  • 建立的一個socket只能建立一個監聽。
  • 建立的一個socket不能夠監聽多個 。
  • 建立多個socket可以建立多個監聽。
  • 建立多個socket可以僅建立一個監聽。

4、ThreadX NetXDUO的TCP Socket資料包申請和釋放問題

  • 函式nx_tcp_socket_receive 會申請一個NX_PACKET資料包用於接收,如果使用者不使用了必須使用函式nx_packet_release釋放。
  • 使用函式nx_tcp_socket_send必須有申請好的NX_PACKET資料包,可以使用函式nx_packet_allocate申請,也可以使用nx_tcp_socket_receive申請的。

特別要注意的地方來了,函式nx_tcp_socket_send呼叫後會釋放nx_packet_allocate或者nx_tcp_socket_receive申請的資料包。無需使用者再去呼叫函式nx_packet_release釋放。

9.2 TCP客戶端API函式

下面一張圖說明ThreadX NetXDUO TCP Socket的各種API玩法:

9.2.1 函式nx_system_initialize

函式原型:

VOID nx_system_initialize(VOID);

函式描述:

NetXDUO初始化,所有其它功能呼叫之前必須優先呼叫此函式。

9.2.2 函式nx_packet_pool_create

函式原型:

UINT nx_packet_pool_create(
                          NX_PACKET_POOL *pool_ptr,
                          CHAR *name,
                          ULONG payload_size,
                          VOID *memory_ptr,
                          ULONG memory_size);                 

函式描述:

此函式用於資料包記憶體池建立

函式引數:

  1. 第1個引數是記憶體池控制塊的地址。
  2. 第2個引數是記憶體池名字。
  3. 第3個引數是記憶體池中每個資料包的位元組數。 此值必須至少為 40 個位元組,並且還必須可以被 4 整除。
  4. 第4個引數是記憶體池中資料地址,此地址必須ULONG對齊。
  5. 第5個引數是記憶體池大小。
  6. 返回值:
  • NX_SUCCESS:(0x00) 建立記憶體池成功。
  • NX_PTR_ERROR:(0x07) 第1個引數地址無效。
  • NX_SIZE_ERROR:(0x09) 第5個引數記憶體池大小無效。
  • NX_CALLER_ERROR:(0x11) 此服務的呼叫方無效。

使用舉例:

  /* 建立記憶體池 */
    status =  nx_packet_pool_create(&pool_0,                 /* 記憶體池控制塊 */
                                    "NetX Main Packet Pool",/* 記憶體池名 */
               1536, /* 記憶體池每個資料包大小,單位位元組此值必須至少為 40 個位元組,並且還必須可以被 4 整除 */
             (ULONG*)(((int)packet_pool_area + 15) & ~15) ,/* 記憶體池地址,此地址必須ULONG對齊 */
               NX_PACKET_POOL_SIZE);                        /* 記憶體池大小 */       

9.2.3 函式nx_ip_create

函式原型:

UINT nx_ip_create(
    NX_IP *ip_ptr, 
    CHAR *name, ULONG ip_address,
    ULONG network_mask, 
    NX_PACKET_POOL *default_pool,
    VOID (*ip_network_driver)(NX_IP_DRIVER *),
    VOID *memory_ptr, 
    ULONG memory_size,
    UINT priority);                    

函式描述:

此函式使用使用者提供的 IP 地址,資料包記憶體記憶體池和網路驅動程式建立 IP 例項。注意,直到 IP任務執行之後,才會呼叫網路驅動。

函式引數:

  1. 第1個引數是建立IP例項的控制塊指標。
  2. 第2個引數是IP例項的名字。
  3. 第3個引數是IP地址。
  4. 第4個引數是子網掩碼
  5. 第5個引數是記憶體池地址。
  6. 第6個引數是網絡卡驅動地址。
  7. 第7個引數是IP任務棧地址
  8. 第8個引數是IP任務棧大小,單位位元組。
  9. 第9個引數是IP任務優先順序。
  10. 返回值
  • NX_SUCCESS:(0x00) 建立 IP 例項成功。
  • NX_NOT_IMPLEMENTED:(0x4A) 未正確配置 NetX Duo 庫。
  • NX_PTR_ERROR:(0x07) IP控制塊地址、網路驅動函式指標、記憶體池地址或任務棧地址無效。
  • NX_SIZE_ERROR:(0x09) 提供的任務棧大小太小。
  • NX_CALLER_ERROR:(0x11) 此服務的呼叫方無效。
  • NX_IP_ADDRESS_ERROR:(0x21) 提供的 IP 地址無效。
  • NX_OPTION_ERROR:(0x21) 提供的 IP 任務優先順序無效。

使用舉例:

/* 例化IP */
    status = nx_ip_create(&ip_0,                                                   /* IP例項控制塊 */                                    
                            "NetX IP Instance 0",                                  /* IP例項名 */     
                            IP_ADDRESS(IP_ADDR0, IP_ADDR1, IP_ADDR2, IP_ADDR3),    /* IP地址 */
                            0xFFFFFF00UL,                                          /* 子網掩碼 */
                            &pool_0,                                               /* 記憶體池 */
                        nx_driver_stm32h7xx,                                   /* 網絡卡驅動 */
                            (UCHAR*)AppTaskNetXStk,                                /* IP任務棧地址 */
                            sizeof(AppTaskNetXStk),                             /* IP任務棧大小,單位位元組 */
                            APP_CFG_TASK_NETX_PRIO);                            /* IP任務優先順序 */

9.2.4 函式nx_arp_enable

函式原型:

UINT nx_arp_enable(
    NX_IP *ip_ptr, 
    VOID *arp_cache_memory,
    ULONG arp_cache_size);                

函式描述:

此函式用於使能ARP地址解析。

函式引數:

  1. ip_ptr:IP例項地址。
  2. arp_cache_memory:ARP快取地址。
  3. arp_cache_size:每個 ARP 條目均為 52 個位元組,因此,ARP 條目總數是52位元組整數倍。
  4. 返回值
  • NX_SUCCESS:(0x00) 啟用 ARP 成功。
  • NX_PTR_ERROR:(0x07) IP例項地址或ARP快取地址無效。
  • NX_SIZE_ERROR:(0x09) 使用者提供的 ARP 快取記憶體太小。
  • NX_CALLER_ERROR:(0x11) 此服務的呼叫方無效。
  • NX_ALREADY_ENABLED:(0x15) 此元件已啟用。

使用舉例:

int32_t tcp_sock;

tcp_sock = netTCP_GetSocket (tcp_cb_server);
    
if (tcp_sock > 0) 
{
res = netTCP_Listen (tcp_sock, PORT_NUM);
}

if(netTCP_SendReady(tcp_sock) == true )
{

}

9.2.5 函式nx_ip_fragment_enable

函式原型:

UINT nx_ip_fragment_enable(NX_IP *ip_ptr);

函式描述:

此函式用於啟用 IPv4 和 IPv6 資料包分段和重組功能。建立 IP 任務時,此服務會自動禁用。

函式引數:

1、第1個引數是IP例項地址。

2、返回值

  • NX_SUCCESS:(0x00) 啟用 IP 分段成功。
  • NX_PTR_ERROR:(0x07) IP 例項地址無效。
  • NX_CALLER_ERROR:(0x11) 此服務的呼叫方無效。
  • NX_NOT_ENABLED:(0x14) IP 分段功能未編譯到 NetX Duo 中。

使用舉例:

    /* 使能fragment */    
status = nx_ip_fragment_enable(&ip_0);

9.2.6 函式nx_tcp_enable

函式原型:

UINT nx_tcp_enable(NX_IP *ip_ptr);

函式描述:

此函式用於使能TCP元件。

函式引數:

1、第1個引數是IP例項地址。

2、返回值

  • NX_SUCCESS:(0x00) 啟用 TCP 成功。
  • NX_ALREADY_ENABLED:(0x15) TCP 已啟用。
  • NX_PTR_ERROR:(0x07) IP 例項地址無效。
  • NX_CALLER_ERROR:(0x11) 此服務的呼叫方無效。

使用舉例:

    /* 使能TCP */
    status =  nx_tcp_enable(&ip_0);

9.2.7 函式nx_tcp_client_socket_bind

函式原型:

UINT nx_tcp_client_socket_bind(
    NX_TCP_SOCKET *socket_ptr,
    UINT port,
    ULONG wait_option);

函式描述:

此函式用於為建立的TCP Socket繫結埠。如果設定的埠號還不可用,可以設定等待時間。

函式引數:

1、第1個引數是TCP Socket指標。

2、第2個引數是繫結的埠,範圍1 -65535。如果設定為NX_ANY_PORT(0x0000),則會搜尋一個可用埠號。

3、第3個引數是埠號不可用時,等待時間定義:

  • NX_NO_WAIT (0x00000000)
  • NX_WAIT_FOREVER (0xFFFFFFFF)
  • 等待時間:(0x00000001 到 0xFFFFFFFE),單位是ThreadX系統時鐘節拍。

4、返回值

  • NX_SUCCESS:(0x00) 繫結TCP Socket成功。
  • NX_ALREADY_BOUND:(0x22) 此TCP Socket已與另一 TCP 埠繫結。
  • NX_PORT_UNAVAILABLE:(0x23) 埠已與其他Socket繫結。
  • NX_NO_FREE_PORTS:(0x45) 沒有可用的埠。
  • NX_WAIT_ABORTED:(0x1A) 已通過呼叫 tx_thread_wait_abort 中止請求。
  • NX_INVALID_PORT:(0x46) 埠無效。
  • NX_PTR_ERROR:(0x07) Socket指標無效。
  • NX_CALLER_ERROR:(0x11) 此服務的呼叫方無效。
  • NX_NOT_ENABLED:(0x14) 此元件尚未啟用。

使用舉例:

    /* 繫結埠 */
    ret =  nx_tcp_client_socket_bind(&TCPSocket, DEFAULT_PORT, NX_WAIT_FOREVER);

    if (ret != NX_SUCCESS)
    {
        Error_Handler(__FILE__, __LINE__);   
}

9.2.8 函式nx_tcp_client_socket_unbind

函式原型:

UINT nx_tcp_client_socket_unbind(NX_TCP_SOCKET *socket_ptr);

函式描述:

此函式用於解除TCP Socket繫結的埠。

函式引數:

1、 第1個引數是TCP Socket指標。

2、 返回值

  • NX_SUCCESS:(0x00) 取消繫結Socket成功。
  • NX_NOT_BOUND:(0x24) Socket未與任何埠繫結。
  • NX_NOT_CLOSED:(0x35) Socket尚未斷開連線。
  • NX_PTR_ERROR:(0x07) Socket指標無效。
  • NX_CALLER_ERROR:(0x11) 此服務的呼叫方無效。
  • NX_NOT_ENABLED (0x14) 尚未啟用此元件。

使用舉例:

    /* 繫結埠 */
    ret =  nx_tcp_client_socket_bind(&TCPSocket, DEFAULT_PORT, NX_WAIT_FOREVER);

    if (ret != NX_SUCCESS)
    {
        Error_Handler(__FILE__, __LINE__);   
    }

9.2.9 函式nx_tcp_client_socket_connect

函式原型:

UINT nx_tcp_client_socket_connect(
    NX_TCP_SOCKET *socket_ptr,
    ULONG server_ip,
    UINT server_port,
    ULONG wait_option);

函式描述:

此函式用於建立TCP客戶端連線遠端伺服器。

函式引數:

1、 第1個引數是TCP Socket指標。

2、 第2個引數是遠端伺服器IP。

3、 第3個引數是遠端埠。

4、 第4個引數連線遠端伺服器的等待選項,支援的引數如下:

  • NX_NO_WAIT (0x00000000)
  • NX_WAIT_FOREVER (0xFFFFFFFF)
  • 等待時間:(0x00000001 到 0xFFFFFFFE),單位是ThreadX系統時鐘節拍。

5、 返回值:

  • NX_SUCCESS:(0x00) 連線套接字成功。
  • NX_NOT_BOUND:(0x24) 套接字未繫結。
  • NX_NOT_CLOSED:(0x35) 套接字未處於關閉狀態。
  • NX_IN_PROGRESS (0x37) 未指定等待,正在嘗試連線。
  • NX_INVALID_INTERFACE:(0x4C) 提供了無效的介面。
  • NX_WAIT_ABORTED:(0x1A) 已通過呼叫 tx_thread_wait_abort 中止所請求的掛起。
  • NX_IP_ADDRESS_ERROR:(0x21) 伺服器 IP 地址無效。
  • NX_INVALID_PORT (0x46) 埠無效。
  • NX_PTR_ERROR:(0x07) 套接字指標無效。
  • NX_CALLER_ERROR:(0x11) 此服務的呼叫方無效。
  • NX_NOT_ENABLED (0x14) 尚未啟用此元件。

使用舉例:

    /* 連線遠端伺服器 */
    ret = nx_tcp_client_socket_connect(&TCPSocket, TCP_SERVER_ADDRESS, TCP_SERVER_PORT, NX_WAIT_FOREVER);

    if (ret != NX_SUCCESS)
    {
        Error_Handler(__FILE__, __LINE__);   
    }

9.2.10 函式nx_tcp_socket_info_get

函式原型:

UINT nx_tcp_socket_info_get(
    NX_TCP_SOCKET *socket_ptr,
    ULONG *tcp_packets_sent,
    ULONG *tcp_bytes_sent,
    ULONG *tcp_packets_received,
    ULONG *tcp_bytes_received,
    ULONG *tcp_retransmit_packets,
    ULONG *tcp_packets_queued,
    ULONG *tcp_checksum_errors,
    ULONG *tcp_socket_state,
    ULONG *tcp_transmit_queue_depth,
    ULONG *tcp_transmit_window,
    ULONG *tcp_receive_window);

函式描述:

用於獲取TCP Socket相關資訊。

函式引數:

  1. 第1個引數是TCP Socket指標。
  2. 第2個引數是傳送的TCP資料包總數目。
  3. 第3個引數是傳送的TCP總位元組數。
  4. 第4個引數是接收的TCP資料包總數目。
  5. 第5個引數是接收的TCP總位元組數。
  6. 第6個引數是重新傳輸的TCP資料包總數目。
  7. 第7個引數是Socket上TCP排隊的TCP資料包總數。
  8. 第8個引數是Socket上有校驗和錯誤的TCP資料包總數。
  9. 第9個引數是Socket當前狀態。
  10. 第10個引數是仍在排隊等待ACK的傳送資料包總數。
  11. 第11個引數是當前傳送視窗大小。
  12. 第12個引數是當前接收視窗大小。
  13. 返回值:
  • NX_SUCCESS:(0x00) 檢索 TCP Socket資訊成功。
  • NX_PTR_ERROR:(0x07) Socket指標無效。
  • NX_CALLER_ERROR:(0x11) 此服務的呼叫方無效。
  • NX_NOT_ENABLED:(0x14) 此元件尚未啟用。

使用舉例:

   /* 獲取socket狀態 */
        nx_tcp_socket_info_get(&TCPSocket,     /* TCP Socket控制塊 */
                               NULL,           /* 傳送的TCP資料包總數目 */
                               NULL,           /* 傳送的TCP總位元組數 */
                               NULL,           /* 接收TCP資料包總數目 */
                               NULL,           /* 接收的TCP總位元組數 */
                               NULL,           /* 重新傳輸的TCP資料包總數目 */
                               NULL,           /* Socket上TCP排隊的TCP資料包總數 */
                               NULL,           /* Socket上有校驗和錯誤的TCP資料包總數 */
                               &socket_state,  /* Socket當前狀態 */
                               NULL,           /* 仍在排隊等待ACK的傳送資料包總數 */
                               NULL,           /* 當前傳送視窗大小 */
                               NULL);          /* 當前接收視窗大小 */

9.2.11 函式nx_tcp_socket_receive

函式原型:

UINT nx_tcp_socket_receive(
    NX_TCP_SOCKET *socket_ptr,
    NX_PACKET **packet_ptr,
    ULONG wait_option);

函式描述:

此函式用於從指定的Socket接收TCP資料,如果指定的Socket上沒有已經排隊的資料,則呼叫方會根據提供的等待選項引數掛起。

函式引數:

1、 第1個引數是TCP Socket指標

2、 第2個引數是TCP資料包指標。

3、 第3個引數是Socket佇列上沒有資料時的處理:

  • NX_NO_WAIT (0x00000000)。
  • NX_WAIT_FOREVER (0xFFFFFFFF)。
  • 以時鐘週期為單位的超時值(0x00000001 到 0xFFFFFFFE)。

4、 返回值:

  • NX_SUCCESS:(0x00) 接收Socket資料成功。
  • NX_NOT_BOUND:(0x24) Socket未繫結。
  • NX_NO_PACKET:(0x01) 未收到任何資料。
  • NX_WAIT_ABORTED:(0x1A) 通過呼叫 tx_thread_wait_abort 中止掛起。
  • NX_NOT_CONNECTED:(0x38) 該Socket不再處於已連線狀態。
  • NX_PTR_ERROR:(0x07) Socket指標或返回資料包指標無效。
  • NX_CALLER_ERROR:(0x11) 此服務的呼叫方無效。
  • NX_NOT_ENABLED:(0x14) 此元件尚未啟用。

注意事項:

  1. 如果返回了 NX_SUCCESS,則應用程式負責:不再需要收到資料包時將其釋放。

使用舉例:

/* 接收TCP客戶端發的TCP資料包 */
ret = nx_tcp_socket_receive(&TCPSocket,        /* TCP Socket控制塊 */
                          &data_packet,      /* 接收到的資料包 */
                           NX_WAIT_FOREVER);  /* 永久等待 */

9.2.12 函式nx_tcp_socket_send

函式原型:

UINT nx_tcp_socket_send(
    NX_TCP_SOCKET *socket_ptr,
    NX_PACKET *packet_ptr,
    ULONG wait_option);

函式描述:

此函式用於TCP Socket資料傳送。 如果接收方最近一次建議的視窗大小低於此請求,則此函式可以根據指定的等待引數掛起。此函式可保證不會將大於 MSS 的資料包資料傳送到 IP 層。

函式引數:

1、 第1個引數是TCP Socket控制代碼。

2、 第2個引數是TCP資料包指標。

3、 第3個引數是傳送的資料包大於接收方視窗大小時的引數處理,支援如下引數:

  • NX_NO_WAIT (0x00000000)
  • NX_WAIT_FOREVER (0xFFFFFFFF)
  • 以時鐘週期為單位的超時值(0x00000001 到 0xFFFFFFFE)

4、 返回值,返回以下幾種狀態值:

  • NX_SUCCESS:(0x00) Socket傳送成功。
  • NX_NOT_BOUND:(0x24) Socket未與任何埠繫結。
  • NX_NO_INTERFACE_ADDRESS:(0x50) 找不到合適的傳出介面。
  • NX_NOT_CONNECTED:(0x38) 套接字不再處於已連線狀態。
  • NX_WINDOW_OVERFLOW:(0x39) 請求大於接收方所播發的視窗大小(以位元組為單位)。
  • NX_WAIT_ABORTED:(0x1A) 已通過呼叫 tx_thread_wait_abort 中止所請求的掛起。
  • NX_INVALID_PACKET:(0x12) 資料包未分配。
  • NX_TX_QUEUE_DEPTH:(0x49) 已達到最大傳輸佇列深度。
  • NX_OVERFLOW:(0x03) 資料包追加指標無效。
  • NX_PTR_ERROR:(0x07) 套接字指標無效。
  • NX_CALLER_ERROR:(0x11) 此服務的呼叫方無效。
  • NX_NOT_ENABLED:(0x14) 此元件尚未啟用。
  • NX_UNDERFLOW:(0x02) 資料包前置指標無效。

注意事項:

  1. 除非返回了錯誤,否則應用程式不應在呼叫此函式後釋放該資料包。這樣做會導致不可預知的結果,因為網路驅動程式還會在傳輸後嘗試釋放該資料包。

使用舉例:

/* 立即將接收到的資料傳送回去 */
ret =  nx_tcp_socket_send(&TCPSocket,       /* TCP Socket控制塊 */
                        data_packet,      /* 資料包 */
                        NX_WAIT_FOREVER); /* 永久等待 */

9.2.13 函式nx_packet_data_retrieve

函式原型:

UINT nx_packet_data_retrieve(
    NX_PACKET *packet_ptr,
    VOID *buffer_start,
    ULONG *bytes_copied);

函式描述:

此函式用於將提供的資料包中的資料複製到提供的緩衝區。複製的實際位元組數由形參bytes_copied 所指向的儲存單元返回。

注意,此函式不會更改該資料包的內部狀態。檢索的資料仍存在於該資料包中。

函式引數:

1、 第1個引數是指向源資料包的指標

2、 第2個引數是目的資料包的地址。

3、 第3個引數是最終複製的位元組數儲存地址。

4、 返回值,返回以下幾種狀態值:

  • NX_SUCCESS:(0x00)複製資料包資料成功。
  • NX_INVALID_PACKET:(0x12) 資料包無效。
  • NX_PTR_ERROR:(0x07) 形參地址無效。

注意事項:

目標緩衝區的大小必須足以容納該資料包的內容。否則記憶體會損壞,導致不可預知的結果。

使用舉例:

/* 獲取客戶端發來的資料 */
 nx_packet_data_retrieve(data_packet,    /* 接收到的資料包 */
                      data_buffer,    /* 解析出資料 */
                      &bytes_read);   /* 資料大小 */

9.2.14 函式nx_packet_release

函式原型:

UINT nx_packet_release(NX_PACKET *packet_ptr);

函式描述:

此函式用於釋放資料包,包括連結到指定資料包的任何其他資料包。如果有其他任務在等待這個資料包,則該任務會獲得該資料包並繼續執行。

函式引數:

1、 第1個引數是資料包地址。

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

  • NX_SUCCESS:(0x00) 釋放資料包成功。
  • NX_PTR_ERROR:(0x07) 資料包指標無效。
  • NX_UNDERFLOW:(0x02) 預置指標小於有效負載開始位置。
  • NX_OVERFLOW:(0x03) 追加指標大於有效負載結束位置。

注意事項:

應用程式必須防止多次釋放同一資料包,否則會導致不可預知的結果。

9.2.15 函式nx_tcp_socket_disconnect

函式原型:

UINT nx_tcp_socket_disconnect(
                 NX_TCP_SOCKET *socket_ptr,
                 ULONG wait_option);

函式描述:

此函式用於斷開已建立的客戶端或伺服器Socket。在伺服器Socket斷開連線後應該有一個取消接受請求,而斷開連線的客戶端Socket會處於準備好接受其他連線請求的狀態。 如果斷開連線過程無法立即完成,則該函式會根據提供的等待選項掛起。

函式引數:

1、 第1個引數是TCP Socket地址。

2、 第2個引數等待斷開連線時,支援的引數:

  • NX_NO_WAIT (0x00000000)。
  • NX_WAIT_FOREVER (0xFFFFFFFF)。
  • 以時鐘週期為單位的超時值(0x00000001 到 0xFFFFFFFE)。

3、 返回值,返回以下幾種狀態值:

  • NX_SUCCESS:(0x00) 斷開Socket連線成功。
  • NX_NOT_CONNECTED:(0x38) 指定的Socket未連線。
  • NX_IN_PROGRESS:(0x37) 斷開連線正在進行。
  • NX_WAIT_ABORTED:(0x1A) 已通過呼叫 tx_thread_wait_abort 中止掛起請求。
  • NX_PTR_ERROR:(0x07) Socket指標無效。
  • NX_CALLER_ERROR:(0x11) 此服務的呼叫方無效。
  • NX_NOT_ENABLED:(0x14) 此元件尚未啟用。

使用舉例:

/* 斷開連線 */
nx_tcp_socket_disconnect(&TCPSocket, 
                        NX_WAIT_FOREVER);

9.3 TCP客戶端的實現方法

9.3.1 NetXDUO初始化

建立TCP伺服器前,要初始化NetX,建立記憶體池,例化IP:

/*
*********************************************************************************************************
*    函 數 名: NetXTest
*    功能說明: TCPnet應用
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/    
void NetXTest(void)
{
    UINT status;
    UINT ret;
    ULONG socket_state;
    UINT old_priority;

    NX_PACKET *data_packet;
    ULONG bytes_read;
    
    ULONG peer_ip_address;
    ULONG peer_port;
    
    
    /* 初始化NetX */
    nx_system_initialize();

    /* 建立記憶體池 */
    status =  nx_packet_pool_create(&pool_0,                 /* 記憶體池控制塊 */
                                     "NetX Main Packet Pool",/* 記憶體池名 */
               1536, /* 記憶體池每個資料包大小,單位位元組此值必須至少為 40 個位元組,並且還必須可以被 4 整除 */
             (ULONG*)(((int)packet_pool_area + 15) & ~15) ,/* 記憶體池地址,此地址必須ULONG對齊 */
               NX_PACKET_POOL_SIZE);                        /* 記憶體池大小 */                  
          
    /* 檢測建立是否失敗 */
    if (status) error_counter++;

    /* 例化IP */
    status = nx_ip_create(&ip_0,                                                   /* IP例項控制塊 */                                    
                            "NetX IP Instance 0",                                  /* IP例項名 */     
                            IP_ADDRESS(IP_ADDR0, IP_ADDR1, IP_ADDR2, IP_ADDR3),    /* IP地址 */
                            0xFFFFFF00UL,                                          /* 子網掩碼 */
                            &pool_0,                                               /* 記憶體池 */
                        nx_driver_stm32h7xx,                                   /* 網絡卡驅動 */
                            (UCHAR*)AppTaskNetXStk,                                /* IP任務棧地址 */
                            sizeof(AppTaskNetXStk),                             /* IP任務棧大小,單位位元組 */
                            APP_CFG_TASK_NETX_PRIO);                            /* IP任務優先順序 */
                            
            
    /* 檢測建立是否失敗 */
    if (status) error_counter++;

    /* 使能ARP,並提供ARP快取 */
    status =  nx_arp_enable(&ip_0,               /* IP例項控制塊 */
                     (void *)arp_space_area,  /* ARP快取地址 */
         sizeof(arp_space_area));   /* 每個 ARP 條目均為 52 個位元組,因此,ARP 條目總數是52位元組整數倍 */

    /* 使能fragment */    
    status = nx_ip_fragment_enable(&ip_0);

    /* 檢測使能成功 */
    if (status) error_counter++;

    /* 使能TCP */
    status =  nx_tcp_enable(&ip_0);

    /* 檢測使能成功 */
    if (status) error_counter++;

    /* 使能UDP  */
    status =  nx_udp_enable(&ip_0);

    /* 檢測使能成功 */
    if (status) error_counter++;

    /* 使能ICMP */
    status =  nx_icmp_enable(&ip_0);

    /* 檢測使能成功 */
    if (status) error_counter++;   
    
    /* NETX初始化完畢後,重新設定優先順序 */
    tx_thread_priority_change(netx_thread_ptr, APP_CFG_TASK_NETX_PRIO1, &old_priority);
tx_thread_priority_change(&AppTaskNetXProTCB, APP_CFG_TASK_NetXPro_PRIO1, &old_priority);

    /* 省略 */
}

程式末尾務優先順序做了特別處理,建立的時候先設定為低優先順序,檢測到網線正常連線並初始了網路後將優先順序設定到正常水平。

9.3.2 TCP客戶端實現

下面是建立TCP客戶端並遠端連線伺服器:

/*
*********************************************************************************************************
*    函 數 名: NetXTest
*    功能說明: TCPnet應用
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/    
void NetXTest(void)
{
  
     /* 省略 */

    /* 建立TCP Socket */
    ret = nx_tcp_socket_create(&ip_0,                 /* IP例項控制塊 */    
                               &TCPSocket,            /* TCP控制塊 */ 
                               "TCP Server Socket",   /* TCP Socket名 */ 
                               NX_IP_NORMAL,          /* IP服務型別 */ 
                               NX_FRAGMENT_OKAY,      /* 使能IP分段 */ 
                               NX_IP_TIME_TO_LIVE,    /*用於定義此資料包在被丟棄之前可通過的路由器數目 */ 
                               4320,                  /* TCP Socket接收佇列中允許的最大位元組數 */ 
                               NX_NULL,               /* 用於在接收流中檢測到緊急資料時呼叫的回撥函式 */
                               NX_NULL);              /* TCP Socket另一端發出斷開連線時呼叫的回撥函式 */
    if (ret)
    {
        Error_Handler(__FILE__, __LINE__);    
    }

    /*
    * 監聽新的連結。
    * 建立回撥TCP_listen_callback表示監聽到新連線。
    */
    ret = nx_tcp_server_socket_listen(&ip_0,                  /* IP例項控制塊 */  
                                  DEFAULT_PORT,           /* 預設埠 */          
                                      &TCPSocket,             /* TCP Socket控制塊 */
                                      MAX_TCP_CLIENTS,        /* 可以監聽的連線數 */
                                      NULL);                  /* 監聽回撥函式 */

    if (ret)
    {
        Error_Handler(__FILE__, __LINE__);
    }

    /* 啟動TCP通訊前,接收新連線 */
    ret = nx_tcp_server_socket_accept(&TCPSocket,         /* TCP Socket控制塊 */
                                       TX_WAIT_FOREVER);  /* 監聽回撥函式 */

    if (ret)
    {
        Error_Handler(__FILE__, __LINE__);
    }

    /* 繫結埠 */
    ret =  nx_tcp_client_socket_bind(&TCPSocket, DEFAULT_PORT, NX_WAIT_FOREVER);

    if (ret != NX_SUCCESS)
    {
        Error_Handler(__FILE__, __LINE__);   
    }

    /* 連線遠端伺服器 */
    ret = nx_tcp_client_socket_connect(&TCPSocket, TCP_SERVER_ADDRESS, TCP_SERVER_PORT, NX_WAIT_FOREVER);

    if (ret != NX_SUCCESS)
    {
        Error_Handler(__FILE__, __LINE__);   
    }

     /* 省略 */
}

9.3.3 TCP迴環通訊實現

迴環的意思就是電腦端網路助手傳送資料給板子後,板子再將資料返回。為了方便大家使用,本例子將接收資料包和傳送資料包分別做了定義:

/*
*********************************************************************************************************
*    函 數 名: NetXTest
*    功能說明: TCPnet應用
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/    
void NetXTest(void)
{
  
     /* 省略 */

while(1)
    {
        TX_MEMSET(data_buffer, '\0', sizeof(data_buffer));

        /* 獲取socket狀態 */
        nx_tcp_socket_info_get(&TCPSocket,     /* TCP Socket控制塊 */
                               NULL,           /* 傳送的TCP資料包總數目 */
                               NULL,           /* 傳送的TCP總位元組數 */
                               NULL,           /* 接收TCP資料包總數目 */
                               NULL,           /* 接收的TCP總位元組數 */
                               NULL,           /* 重新傳輸的TCP資料包總數目 */
                               NULL,           /* Socket上TCP排隊的TCP資料包總數 */
                               NULL,           /* Socket上有校驗和錯誤的TCP資料包總數 */
                               &socket_state,  /* Socket當前狀態 */
                               NULL,           /* 仍在排隊等待ACK的傳送資料包總數 */
                               NULL,           /* 當前傳送視窗大小 */
                               NULL);          /* 當前接收視窗大小 */

        /* 如果連線還沒有建立,繼續接受新連線,成功的話開啟接收資料 */
        if(socket_state != NX_TCP_ESTABLISHED)
        {
            /* 繫結埠 */
            ret =  nx_tcp_client_socket_bind(&TCPSocket, DEFAULT_PORT, NX_WAIT_FOREVER);

            if (ret != NX_SUCCESS)
            {
                Error_Handler(__FILE__, __LINE__);   
            }

            /* 連線遠端伺服器 */
            ret = nx_tcp_client_socket_connect(&TCPSocket, TCP_SERVER_ADDRESS, 
TCP_SERVER_PORT, NX_WAIT_FOREVER);

            if (ret != NX_SUCCESS)
            {
                Error_Handler(__FILE__, __LINE__);   
            }
        }
        
        if((socket_state == NX_TCP_ESTABLISHED)&&(ret == NX_SUCCESS))
        {
                
            /* 接收TCP客戶端發的TCP資料包 */
            ret = nx_tcp_socket_receive(&TCPSocket,        /* TCP Socket控制塊 */
                                        &RecPacket,      /* 接收到的資料包 */
                                        NX_WAIT_FOREVER);  /* 永久等待 */

            if (ret == NX_SUCCESS)
            {
                
                /* 獲取客戶端的IP地址和埠 */
                nx_tcp_socket_peer_info_get(&TCPSocket,       /* TCP Socket控制塊 */ 
                                            &peer_ip_address, /* 遠端IP地址 */ 
                                            &peer_port);      /* 遠端埠號 */

                /* 獲取客戶端發來的資料 */
                nx_packet_data_retrieve(RecPacket,    /* 接收到的資料包 */
                                        data_buffer,    /* 解析出資料 */
                                        &bytes_read);   /* 資料大小 */

                /* 列印接收到資料 */
                PRINT_DATA(peer_ip_address, (unsigned int)peer_port, data_buffer);

                nx_packet_release(RecPacket);
                
                /* 申請傳送資料包 */
                ret = nx_packet_allocate(&pool_0, &TraPacket, NX_TCP_PACKET, TX_WAIT_FOREVER);

                if (ret)
                {
                    Error_Handler(__FILE__, __LINE__);  
                }

                sprintf((char *)sendbuf, "sendbuf = %d\r\n", count++);
                
                /*將要傳送的資料附加到TraPacket */
                ret = nx_packet_data_append(TraPacket, (VOID *)sendbuf, strlen((char *)sendbuf), 
&pool_0, TX_WAIT_FOREVER);

                if (ret)
                {
                    Error_Handler(__FILE__, __LINE__);
                }
    
                /* 傳送資料,注意傳送後,此函式會釋放資料包 */
                ret =  nx_tcp_socket_send(&TCPSocket,   
                                           TraPacket,       
                                            NX_WAIT_FOREVER); 
                
                
                if (ret)
                {
                    //Error_Handler(__FILE__, __LINE__);
                }
                
            }
            else
            {
                /* 釋放資料包 */
                nx_packet_release(RecPacket);
                
                /* 斷開連線 */
                nx_tcp_socket_disconnect(&TCPSocket, 
                                         NX_WAIT_FOREVER);
                
                /* disconnect the socket */
                nx_tcp_client_socket_unbind(&TCPSocket);
            }
        }
    }      

     /* 省略 */
}

9.4 網路除錯助手和板子的除錯操作步驟

我們這裡使用下面這款除錯助手,當然,任何其它網路除錯助手均可,不限制:

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

9.4.1 測試使用的DM916X網口並注意跳線帽

測試時,網線要插到DM916X網口上:

特別注意此處跳線帽的位置,要短接PG11:

9.4.2 RJ45網路變壓器插座上綠燈和黃燈現象

各種網絡卡、交換機等網路裝置都不一樣,一般來講:綠燈分為亮或不亮(代表網路速度),黃燈分為閃爍或不閃爍(代表是否有資料收發)。

綠燈:長亮代表100M; 不亮代表10M。

黃燈:長亮代表無資料收發; 閃爍代表有資料收發。

也有些千兆網絡卡的燈以顏色區分,不亮代表10M / 綠色代表100M / 黃色代表1000M。現在10M的網路基本看不到了,如果一個燈長亮,基本可以說明100M網路或更高,而另一個燈時而閃爍,那代表有資料收發,具體要看網路裝置了。甚至有些低等網絡卡如TP-LINK,只有一個燈,亮代表連通,閃爍代表資料收發。

對於開發板上面的RJ45網路變壓器插座上面的燈而言,綠燈代表資料收發,長亮的話表示無資料收發,閃爍代表有資料收發。黃燈代表網路速度,長亮代表100M,不亮代表10M。

9.4.3 第1步,設定板子IP地址

我們這裡使用使用固定IP(或者說靜態IP一個意思),設定也比較省事。我們這裡以開發板和電腦直連的方式進行說明,即通過一根網線直接將開發板的網口和電腦端的網口連線起來即可。如果大家使用的是筆記本,強烈推薦測試期間將筆記本的WIFI網路禁止,各種代理軟體和虛擬網絡卡也暫時關閉。等測試完畢了再逐一開啟,檢視是否有問題。

對於固定IP方式,也可以接到路由器或者交換機上面測試,特別注意板子設定的IP地址不要跟路由器或者交換機上其它裝置的IP衝突了,測試階段還是建議採用電腦直連方式,跑通了再跑其它方式。

在檔案demo_dm9162_netx.h中設定IP地址,具體配置如下(大家更新自己的情況修改):

/*
*********************************************************************************************************
*                                        IP相關
*********************************************************************************************************
*/
#define DEFAULT_PORT                    1000    /* TCP伺服器監聽埠號 */

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

9.4.4 第2步,設定電腦IP地址

一定要將電腦端的IP地址設定到跟開發板在一個IP段,即都是192.168.28.X。第2步中已經將開發板的IP設定為192.168.28.245,我們這裡就將電腦的IP設定為192.168.28.221。我這裡是WIN7 64bit系統。

(1)右擊桌面上的“網路”圖示,選擇屬性。

(2)彈出的介面中選項“本地連線”

(3)選擇“屬性(P)”

(4)雙擊“Internet協議版本4(TCP/Ipv4)”選項。

(5)配置IP地址、子網掩碼和預設閘道器,DNS無需配置,記得點選確定

(6)點選了“確定”按鈕後,退回到之前的介面,這裡的“確定”按鈕不要忘了點選:

9.4.5 第3步,測試ping是否成功

下載例程到開發板,然後ping 192.168.28.245,檢視是否連線上。

(1)WIN+R組合鍵開啟“執行”視窗,輸入cmd。

(2)輸入ping 192.168.28.245後,回車,也是可以的。

收發相同,沒有資料丟失,說明ping命令也是成功的。

9.4.6 第4步,在程式中配置要訪問的遠端IP地址和埠

根據第2步設定的電腦端IP地址,需要大家配置程式中app_tcpnet_lib.c檔案開頭的巨集定義,其中IP地址填前面獲取的192.168.28.146,大家要根據電腦實際的IP地址填寫。而埠號,我們這裡隨意配置一個即可,配置為1001,後面電腦端使用網路除錯助手建立TCP伺服器時,務必要跟這個埠號統一:

/* 遠端伺服器埠和IP */
#define TCP_SERVER_PORT                 1001
#define TCP_SERVER_ADDRESS              IP_ADDRESS(192, 168, 28, 146)

9.4.7 第5步,網路除錯助手建立TCP伺服器

  • 開啟除錯助手,點選左上角建立伺服器:

  • 彈出如下介面,指定IP設定為192.168.28.146,一定要跟第2步設定的電腦IP地址一致,埠號1001,最後點選確定:

  • 建立後的介面效果如下:

  • 點選啟動伺服器:

9.4.8 第6步,建立TCP客戶端連線TCP伺服器

如果開發板下載了TCP客戶端的程式,並且開發板已經上電,可以看到客戶端連線已經加入:

跟我們在程式中設定的埠號,即app_tcpnet_lib.c檔案開頭的巨集定義:

#define DEFAULT_PORT 1000是一致的。

9.4.9 第5步,TCP客戶端迴環測試

板子和網路除錯助手建立連線後就可以相互收發資料了。

板子端接收到字元做了個簡單的展示(波特率115200,資料位8,奇偶校驗位無,停止位1):

9.5 實驗例程

配套例子:

V5-2403_ThreadX NetXDUO TCP Client

實驗目的:

  1. 學習ThreadX NetXDUO TCP 客戶端實現

實驗內容:

  1. 共建立瞭如下幾個任務,通過按下按鍵K1可以通過串列埠列印任務堆疊使用情況

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

OS CPU Usage = 1.31%

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

任務優先順序 任務棧大小 當前使用棧 最大棧使用 任務名

Prio StackSize CurStack MaxStack Taskname

2 4092 303 459 App Task Start

30 1020 303 303 App Task STAT

31 1020 87 71 App Task IDLE

5 4092 311 551 App Msp Pro

7 4092 303 719 App Task UserIF

6 4092 255 359 App NETX Pro

3 4092 415 535 NetX IP Instance 0

0 1020 191 235 System Timer Thread

串列埠軟體可以使用SecureCRT或者H7-TOOL RTT檢視列印資訊。

App Task Start任務 :啟動任務,這裡用作BSP驅動包處理。

App Task MspPro任務 :訊息處理。

App Task UserIF任務 :按鍵訊息處理。

App Task COM任務 :這裡用作LED閃爍。

System Timer Thread任務:系統定時器任務

操作說明:

1、由於程式使用了DWT時鐘週期計數器,程式下載後,請將板子重新上電使用,防止DWT時鐘週期計數器沒有正常復位。

2、NetX網路協議棧操作:

(1) 預設IP地址192.168.28.245,在demo_dm9162_netx.c開頭定義,使用者可根據需要修改。

(2) 可以在電腦端用網路除錯軟體建立TCP伺服器,埠號1001。

(3) 實現了一個簡單的迴環通訊,使用者使用上位機發送的資料,然後板子返回另外的資料。

串列埠列印資訊方式(AC5,AC6和IAR):

波特率 115200,資料位 8,奇偶校驗位無,停止位 1

9.6 總結

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

微信公眾號:armfly_com 安富萊論壇:www.armbbs.cn 安富萊淘寶:https://armfly.taobao.com