第 2 章 - Azure RTOS NetX Duo Telnet 的安装和使用

本章介绍与 Azure RTOS NetX Duo Telnet 组件的安装、设置和使用相关的各种问题。

产品分发

可在 https://github.com/azure-rtos/netxduo 获取 Azure RTOS NetX Duo Telnet 包。 该软件包中包含以下文件:

  • nxd_telnet_client.h:适用于 NetX Duo 的 Telnet 客户端的头文件
  • nxd_telnet_client.c:适用于 NetX Duo 的 Telnet 客户端的 C 源文件
  • nxd_telnet_server.h:适用于 NetX Duo 的 Telnet 服务器的头文件
  • nxd_telnet_server.c:适用于 NetX Duo 的 Telnet 服务器的 C 源文件
  • nxd_telnet.pdf:适用于 NetX Duo 的 Telnet 的 PDF 说明
  • demo_netxduo_telnet.c:NetX Duo Telnet 演示

Telnet 安装

若要使用适用于 NetX Duo 的 Telnet,应将之前提到的全部分发文件复制到 NetX Duo 所安装的目录。 例如,如果在目录“\threadx\arm7\green”中安装了 NetX Duo,则可以将 nxd_telnet_client.h、nxd_telnet_client.c、nxd_telnet_server.c 和 nxd_telnet_server.h 复制到此目录中 。

使用 Telnet

使用适用于 NetX Duo 的 Telnet 是很简单的。 基本上来说,在包含 tx_api.h 和 nx_api.h 后,应用程序代码必须包括 nxd_telnet_client.h(适用于 Telnet 客户端应用程序)或 nxd_telnet_server(适用于 Telnet 服务器应用程序),以便使用 ThreadX 和 NetX Duo 。 在包含标头后,应用程序代码即可调用本指南后文所指定的 Telnet 函数。 在生成过程中,应用程序还必须包含 nxd_telnet_client.c 和 nxd_telnet_server.c。 这些文件的编译方式必须与其他应用程序文件相同,并且其对象窗体必须与应用程序的文件链接起来。 以上就是使用 NetX Duo Telnet 所需满足的条件。

如果不需要 Telnet 客户端功能,可以省略 nxd_telnet_client.c 文件。

另请注意,由于 Telnet 采用 NetX Duo TCP 服务,因此在使用 Telnet 之前,必须通过 nx_tcp_enable 调用来启用 TCP。

小型示例系统

下面的图 1.1 举例说明了 NetX Duo Telnet 的用法。 在此示例中,在第 7 行和第 8 行引入了 Telnet 包含文件。 接下来,在第 146 行的“tx_application_define”中创建了 Telnet 服务器。 请注意,Telnet 服务器和客户端控制块以前在第 23-24 行中定义为全局变量。

在启动 Telnet 服务器或客户端之前,必须使用 NetX Duo 验证其 IP 地址。 对于 IPv4 连接,其完成方式是直接短暂等待 NetX 驱动程序执行系统初始化,这是在 166 行上完成的。 对于 IPv6 连接,需要启用 IPv6 和 ICMPv6,这是在 171-172 行上完成的。 客户端在第 181-186 行上,在主接口上设置其全局和链接本地 IPv6 地址,并等待 NetX Duo 验证在后台完成。 服务器则在 192–198 行上,在其主接口上设置其全局和链接本地地址。 请注意,nxd_ipv6_global_address_set 和 nxd_ipv6_linklocal_address_set 这两个服务将替换为 nxd_ipv6_address_set 服务 。 之前的两个服务仍可用于旧版的 NetX Duo 应用程序,但最终将弃用。 建议开发人员改用 nxd_ipv6_address_set。

使用 NetX Duo 验证 IP 地址成功后,将使用 nxd_telnet_server_start 服务在第 215 行启动 Telnet 服务器。 将在第 226 行,使用 nx_telnet_client_create 服务创建 Telnet 客户端。 然后,它会分别使用 nxd_telnet_client_connect 服务和 nx_telnet_client_connect 服务在第 242 行(适用于 IPv4 应用程序)和第 238 行(适用于 IPv6 应用程序)连接到 Telnet 服务器。 成功验证并连接到服务器后,它会在断开连接前进行一定次数的通信。

/* This is a small demo of TELNET on the high-performance NetX Duo TCP/IP stack.  
       This demo relies on ThreadX and NetX Duo to show a simple TELNET connection,
       send, server echo, and then disconnection from the TELNET server.  */
    
#include  "tx_api.h"
#include  "nx_api.h"
#include  "nxd_telnet_client.h"
#include  "nxd_telnet_server.h"
#define     DEMO_STACK_SIZE         4096    
   
/* Define the ThreadX and NetX object control blocks...  */
TX_THREAD               test_thread;
NX_PACKET_POOL          pool_server;
NX_PACKET_POOL          pool_client;
NX_IP                   ip_server;
NX_IP                   ip_client;
   
/* Define TELNET objects.  */

NX_TELNET_SERVER        my_server;
NX_TELNET_CLIENT        my_client;


#ifdef FEATURE_NX_IPV6

/* Define NetX Duo IP address for the NetX Duo Telnet Server and Client. */

NXD_ADDRESS     server_ip_address;
NXD_ADDRESS     client_ip_address;

#endif

#define         SERVER_ADDRESS          IP_ADDRESS(1,2,3,4)
#define         CLIENT_ADDRESS          IP_ADDRESS(1,2,3,5)

/* Define the counters used in the demo application...  */
ULONG                   error_counter;

/* Define timeout in ticks for connecting and sending/receiving data. */

#define                 TELNET_TIMEOUT  200

/* Define function prototypes.  */

void    thread_test_entry(ULONG thread_input);
void    _nx_ram_network_driver(struct NX_IP_DRIVER_STRUCT *driver_req);


/* Define the application's TELNET Server callback routines.  */

void    telnet_new_connection(NX_TELNET_SERVER *server_ptr, UINT 
                              logical_connection); 
void    telnet_receive_data(NX_TELNET_SERVER *server_ptr, UINT logical_connection, 
                            NX_PACKET *packet_ptr);
void    telnet_connection_end(NX_TELNET_SERVER *server_ptr, UINT 
                              logical_connection);

/* Define main entry point.  */

int main()
{

    /* Enter the ThreadX kernel.  */
    tx_kernel_enter();
}

/* Define what the initial system looks like.  */
void    tx_application_define(void *first_unused_memory)
{

UINT    status;
CHAR    *pointer;
UINT    iface_index, address_index;
    
    /* Setup the working pointer.  */
    pointer =  (CHAR *) first_unused_memory;
    
    /* Create the main thread.  */
    tx_thread_create(&test_thread, "test thread", thread_test_entry, 0,  
                     pointer, DEMO_STACK_SIZE, 
                     2, 2, TX_NO_TIME_SLICE, TX_AUTO_START);
    pointer =  pointer + DEMO_STACK_SIZE;
    
    /* Initialize the NetX system.  */
    nx_system_initialize();
    
    /* Create packet pool.  */
    nx_packet_pool_create(&pool_server, "Server NetX Packet Pool", 600, pointer, 8192);
    pointer = pointer + 8192;
    
    /* Create an IP instance.  */
    nx_ip_create(&ip_server, "Server NetX IP Instance", SERVER_ADDRESS, 
                 0xFFFFFF00UL, &pool_server, _nx_ram_network_driver,
                 pointer, 4096, 1);
    
    pointer =  pointer + 4096;
    
    /* Create another packet pool. */
    nx_packet_pool_create(&pool_client, "Client NetX Packet Pool", 600, 
                          pointer, 8192);
    pointer = pointer + 8192;
    
    /* Create another IP instance.  */
    nx_ip_create(&ip_client, "Client NetX IP Instance", CLIENT_ADDRESS, 
                 0xFFFFFF00UL, &pool_client, _nx_ram_network_driver, 
                 pointer, 4096, 1);
    
    pointer = pointer + 4096;
    
    /* Enable ARP and supply ARP cache memory for IP Instance 0.  */
    nx_arp_enable(&ip_server, (void *) pointer, 1024);
    pointer = pointer + 1024;
    
    /* Enable ARP and supply ARP cache memory for IP Instance 1.  */
    nx_arp_enable(&ip_client, (void *) pointer, 1024);
    pointer = pointer + 1024;
    
    /* Enable TCP processing for both IP instances.  */
    nx_tcp_enable(&ip_server);
    nx_tcp_enable(&ip_client);

#ifdef FEATURE_NX_IPV6

    /* Next set the NetX Duo Telnet Server and Client addresses. */
    server_ip_address.nxd_ip_address.v6[3] = 0x105;
    server_ip_address.nxd_ip_address.v6[2] = 0x0;
    server_ip_address.nxd_ip_address.v6[1] = 0x0000f101;
    server_ip_address.nxd_ip_address.v6[0] = 0x20010db1;
    server_ip_address.nxd_ip_version = NX_IP_VERSION_V6;

    client_ip_address.nxd_ip_address.v6[3] = 0x101;
    client_ip_address.nxd_ip_address.v6[2] = 0x0;
    client_ip_address.nxd_ip_address.v6[1] = 0x0000f101;
    client_ip_address.nxd_ip_address.v6[0] = 0x20010db1;
    client_ip_address.nxd_ip_version = NX_IP_VERSION_V6;

#endif

    /* Create the NetX Duo TELNET Server.  */
    status =  nx_telnet_server_create(&my_server, "Telnet Server", &ip_server, 
                                      pointer, 2048, telnet_new_connection, telnet_receive_data, 
                                      telnet_connection_end);
    
    /* Check for errors.  */
    if (status)
        error_counter++;
    
    return;
}

/* Define the test thread.  */
void    thread_test_entry(ULONG thread_input)
{

NX_PACKET   *my_packet;
UINT        status;
    
    /* Allow other threads (e.g. IP thread task) to run first. */
    tx_thread_sleep(100);
    
    #ifdef FEATURE_NX_IPV6
    /* Here's where we make the Telnet Client IPv6 enabled. */
    nxd_ipv6_enable(&ip_client);
    nxd_icmp_enable(&ip_client);     
    
    /* Wait till the IP task thread initializes the system. */
    tx_thread_sleep(100);
        
    /* Set up the Client addresses on the Client IP for the primary interface. */
    if_index = 0;
    
    status = nxd_ipv6_address_set(&ip_ client, iface_index, NX_NULL, 10, 
                                  &address_index);
    status = nxd_ipv6_address_set(&ip_ client, iface_index, & client _ip_address, 
                                   64, &address_index);
        
    /* Allow NetX Duo time to validate addresses. */
    tx_thread_sleep(400);
    
    /* Set up the Server addresses on the Client IP. */
    iface_index = 0;
    status = nxd_ipv6_address_set (&ip_server, iface_index, NX_NULL, 10, 
                                   &address_index);
    
    status = nxd_ ipv6_address _set(&ip_server, iface_index, & server _ip_address, 
                                     64, &address_index);
        
    /* Allow NetX Duo time to validate addresses. */     
    tx_thread_sleep(400);
    
    #endif
    
    /* Start the TELNET Server.  */
    status =  nx_telnet_server_start(&my_server);
    
    /* Check for errors.  */
    if (status != NX_SUCCESS)
    {
        return;
    }
    
    /* Create a TELENT client instance.  */
    status =  nx_telnet_client_create(&my_client, "My TELNET Client", 
                                      &ip_client, 600);
    
    /* Check status.  */
    if (status != NX_SUCCESS)
    {
        return;
    }
    
    #ifdef FEATURE_NX_IPV6
    
        /* Connect the TELNET client to the TELNET Server at port 23.  */
        status =  nxd_telnet_client_connect(&my_client, &server_ip_address, 23, 
                                             TELNET_TIMEOUT);
    
    #else
        /* Connect the TELNET client to the TELNET Server at port 23.  */
        status =  nx_telnet_client_connect(&my_client, SERVER_ADDRESS, 23,
                                            TELNET_TIMEOUT);
    #endif
    
    /* Check status.  */
    if (status != NX_SUCCESS)
    {
        return;
    }
    
    /* Allocate a packet.  */
    status =  nx_packet_allocate(&pool_client, &my_packet, NX_TCP_PACKET, 
                                  NX_WAIT_FOREVER);

    /* Check status.  */
    if (status != NX_SUCCESS)
    {
        return;
    }
    
    /* Build a simple 1-byte message.  */
    nx_packet_data_append(my_packet, "a", 1, &pool_client, NX_WAIT_FOREVER);
    
    /* Send the packet to the TELNET Server.  */
    status =  nx_telnet_client_packet_send(&my_client, my_packet, TELNET_TIMEOUT);
    
    /* Check status.  */
    if (status != NX_SUCCESS)
    {
        return;
    }
    
    /* Pickup the Server header.  */
    status =  nx_telnet_client_packet_receive(&my_client, &my_packet, 
                                               TELNET_TIMEOUT);

    /* Check status.  */
    if (status != NX_SUCCESS)
    {
        return;
    }
    
    /* At this point the packet should contain the Server's banner
        message sent by the Server callback function below.  Just
        release it for this demo.  */
    nx_packet_release(my_packet);
    
    /* Pickup the Server echo of the character.  */
    status =  nx_telnet_client_packet_receive(&my_client, &my_packet, 
                                               TELNET_TIMEOUT);
    
    /* Check status.  */
    if (status != NX_SUCCESS)
    {
        return;
    }
    
    /* At this point the packet should contain the character 'a' that
        we sent earlier.  Just release the packet for now.  */
    nx_packet_release(my_packet);
    
    /* Now disconnect form the TELNET Server.  */
    status =  nx_telnet_client_disconnect(&my_client, TELNET_TIMEOUT);
    
    /* Check status.  */
    if (status != NX_SUCCESS)
    {
        return;
    }

    /* Delete the TELNET Client.  */
    status =  nx_telnet_client_delete(&my_client);
    
    /* Check status.  */
    if (status != NX_SUCCESS)
    {
        return;
    }
}

/* This routine is called by the NetX Telnet Server whenever a new Telnet client 
    connection is established.  */
void  telnet_new_connection(NX_TELNET_SERVER *server_ptr, UINT logical_connection)
{

UINT        status;
NX_PACKET   *packet_ptr;

    /* Allocate a packet for client greeting. */
    status =  nx_packet_allocate(&pool_server, &packet_ptr, NX_TCP_PACKET, 
                                  NX_NO_WAIT);

    if (status != NX_SUCCESS)
    {
        error_counter++;
        return;
    }

    /* Build a banner message and a prompt.  */
    nx_packet_data_append(packet_ptr,"**** Welcome to NetX TELNET Server ****\r\n\r\n\r\n", 45,                            
                         &pool_server, NX_NO_WAIT);

    nx_packet_data_append(packet_ptr, "NETX> ", 6, &pool_server, NX_NO_WAIT);
    
    /* Send the packet to the client.  */
    status =  nx_telnet_server_packet_send(server_ptr, logical_connection, 
                                           packet_ptr, TELNET_TIMEOUT);

    if (status != NX_SUCCESS)
    {
        error_counter++;
        nx_packet_release(packet_ptr);
    }
    return;
}

/* This routine is called by the NetX Telnet Server whenever data is present on a 
    Telnet client connection.  */          
void  telnet_receive_data(NX_TELNET_SERVER *server_ptr, UINT logical_connection,
                          NX_PACKET *packet_ptr)
{

UINT    status;
UCHAR   alpha;

    /* This demo echoes the character back; on <cr,lf> sends a new prompt back to 
        the client.  A real system would likely buffer the character(s) received in a 
        buffer associated with the supplied logical connection and process it.  */

    /* Just throw away carriage returns.  */
    if ((packet_ptr -> nx_packet_prepend_ptr[0] == '\r') && (packet_ptr -> nx_packet_length == 1))
    {
        printf("telnet server received just a CRLF\n");

        nx_packet_release(packet_ptr);
        return;
    }

    /* Setup new line on line feed.  */
    if ((packet_ptr -> nx_packet_prepend_ptr[0] == '\n') || (packet_ptr -> nx_packet_prepend_ptr[1] == '\n'))
    {
        /* Clean up the packet.  */
        packet_ptr -> nx_packet_length =  0;
        packet_ptr -> nx_packet_prepend_ptr =  packet_ptr -> nx_packet_data_start + NX_TCP_PACKET;
        packet_ptr -> nx_packet_append_ptr =   packet_ptr -> nx_packet_data_start + NX_TCP_PACKET;

        /* Build the next prompt.  */
        nx_packet_data_append(packet_ptr, "\r\nNETX> ", 8, &pool_server, 
                              NX_NO_WAIT);

        /* Send the packet to the client.  */
        status =  nx_telnet_server_packet_send(server_ptr, logical_connection, 
                                               packet_ptr, TELNET_TIMEOUT);

        if (status != NX_SUCCESS)
        {
            error_counter++;
            nx_packet_release(packet_ptr);
        }
        return;
    }

    /* Pickup first character (usually only one from client).  */
    alpha =  packet_ptr -> nx_packet_prepend_ptr[0];

    /* Echo character.  */
    status =  nx_telnet_server_packet_send(server_ptr, logical_connection, 
                                           packet_ptr, TELNET_TIMEOUT);

    if (status != NX_SUCCESS)
    {
        error_counter++;
        nx_packet_release(packet_ptr);
    }

    /* Check for a disconnection.  */
    if (alpha == 'q')
    {
        /* Initiate server disconnection.  */
        nx_telnet_server_disconnect(server_ptr, logical_connection);
    }
}


/* This routine is called by the NetX Telnet Server when the client disconnects.  */
void  telnet_connection_end(NX_TELNET_SERVER *server_ptr, UINT logical_connection)
{
    /* Cleanup any application specific connection or buffer information.  */
    return;
}

配置选项

构建适用于 NetX Duo 的 Telnet 时,有多个配置选项可用。 这些 #define 可以在包含 nxd_telnet_server.h 和 nxd_telnet_client.h 之前由应用程序设置 。

下面是所有选项的列表,其中包含每个选项的详细说明:

  • NX_DISABLE_ERROR_CHECKING:如果定义此选项,则会删除基本的 Telnet 错误检查。 通常会在调试应用程序后使用此选项。
  • NX_TELNET_MAX_CLIENTS:服务器线程支持的 Telnet 客户端的最大数目。 默认情况下,此值定义为 4,用于一次指定最多 4 个客户端。
  • NX_TELNET_SERVER_PRIORITY:Telnet 服务器线程的优先级。 默认情况下,此值定义为 16,表示将优先级指定为 16。
  • NX_TELNET_TOS:Telnet TCP 请求所需的服务的类型。 默认情况下,此值定义为 NX_IP_NORMAL,指示
    正常的 IP 数据包服务。
  • NX_TELNET_FRAGMENT_OPTION:用于 Telnet TCP 请求的分段启用。 默认情况下,此值为 NX_DONT_FRAGMENT,表示禁用 Telnet TCP 分段。
  • NX_TELNET_SERVER_WINDOW_SIZE:服务器套接字窗口大小。 默认情况下,此值为 2048 个字节。
  • NX_TELNET_TIME_TO_LIVE:指定数据包在被丢弃之前可通过的路由器数目。 默认值设置为 0x80。
  • NX_TELNET_SERVER_TIMEOUT:指定内部服务将暂停的 ThreadX 时钟周期数。 默认情况下,该值设置为 10 秒。
  • NX_TELNET_ACTIVITY_TIMEOUT:指定在服务器断开客户端连接之前在无任何活动的情况下可经过的秒数。 默认值设置为 600 秒。
  • NX_TELNET_TIMEOUT_PERIOD:指定客户端活动超时检查操作之间间隔的秒数。 默认值设置为 60 秒。
  • NX_TELNET_SERVER_OPTION_DISABLE:如果定义,将禁用 Telnet 选项协商。 默认不定义此选项。
  • NX_TELNET_SERVER_USER_CREATE_PACKET_POOL:如果定义,则必须在外部创建 Telnet 服务器数据包池。 这仅在未定义 NX_TELNET_SERVER_OPTION_DISABLE 的情况下有意义。 默认情况下不定义此选项,Telnet 服务器线程会创建自己的数据包池。
  • NX_TELNET_SERVER_PACKET_PAYLOAD:定义 Telnet 服务器创建的用于选项协商的数据包有效负载的大小。 请注意,Telnet 服务器仅在未定义 NX_TELNET_SERVER _OPTION_DISABLE 选项(启用了 Telnet 选项)的情况下创建此数据包池。 此选项的默认值为 300。
  • NX_TELNET_SERVER_PACKET_POOL_SIZE:定义用于 Telnet 协商的 Telnet 服务器数据包池的大小。 请注意,Telnet 服务器仅在未定义 NX_TELNET_SERVER _OPTION_DISABLE 选项(启用了 Telnet 选项)的情况下创建此数据包池。 此选项的默认值为 2048(~5-6 个数据包)。