第 2 章 - 安装和使用 NetX Duo SMTP 客户端

本章介绍与 NetX Duo SMTP 客户端组件的安装、设置和使用相关的各种问题。

NetX Duo SMTP 客户端安装

可通过 https://github.com/azure-rtos/netxduo 获得 NetX Duo SMTP 客户端。 该软件包中包含以下文件:

  • nxd_smtp_client.c:NetX Duo SMTP 客户端 API 的 C 源文件
  • nxd_smtp_client.h:NetX Duo SMTP 客户端 API 的 C 头文件
  • demo_netxduo_smtp_client.c:NetX Duo SMTP 客户端演示
  • nxd_smtp_client.pdf User Guide for:NetX Duo SMTP 客户端 API

若要使用 NetX Duo SMTP 客户端 API,可将之前提到的整个分发包复制到 NetX Duo 的安装目录中。 例如,如果 NetX Duo 安装在“c:\myproject”目录中,则应将 nxd_smtp_client.h 和 nxd_smtp_client.c 文件复制到该目录中 。

使用 NetX Duo SMTP 客户端

若要创建 NetX Duo SMTP 客户端应用程序,必须先生成 ThreadX 和 NetX Duo 库,并将它们添加到生成项目中。 然后,应用程序必须在其应用程序源代码中添加 tx*_api.h* 和 nx_api.h。 这样将会启用 ThreadX 和 NetX Duo 服务。 还必须在 tx_api.h 和 nx_api.h 后面添加 nxd_smtp_client.c 和 nxd_smtp_client.h 才能使用 SMTP 客户端服务 。

这些文件的编译方式必须与其他应用程序文件相同,并且对象代码必须与应用程序的文件链接起来。 这就是创建 NetX Duo SMTP 客户端应用程序所需的全部内容。

小型示例系统

下面的图 1 举例说明了 NetX Duo SMTP 客户端的用法。 在第 68 行,使用 nx_packet_pool_create 服务创建 IP 实例的数据包池,该池的数据包有效负载很小。 这是因为 IP 实例只发送不需要大量有效负载的控制数据包。 在第 84 行创建 SMTP 客户端数据包池,它用于向服务器和消息数据传输 SMTP 客户端消息。 它的数据包有效负载要大得多。 在第 118 行使用相同的数据包池创建 IP 实例。 在第 130 行,在 IP 实例上启用 SMTP 协议所需的 TCP。

在应用程序线程中,使用 nxd_smtp_client_create 服务在第 170 行创建 SMTP 客户端。 虽然此示例仅限于 IPv4,但 nxd_smtp_client_create 服务支持 IPv4 和 IPv6 SMTP 服务器连接。 然后,系统将电子邮件提交给 SMTP 客户端,以在第 184 行使用 nx_smtp_mail_send 服务传输。 注意,带邮件内容标头的主题行与邮件正文是分开创建的。 另请注意,发送邮件请求仅接受一个假定语法正确的收件人邮件地址。

然后,应用程序在第 200 行终止 SMTP 客户端。 nx_smtp_client_delete 服务检查套接字连接是否关闭,以及端口是否未绑定。 注意,如果不再需要使用数据包池,则 SMTP 客户端应用程序可将其删除。

/*
   demo_netxduo_smtp_client.c

   This is a small demo of the NetX Duo SMTP Client on the high-performance NetX
   Duo TCP/IP stack.  This demo relies on Thread, NetX Duo and SMTP Client API to
   perform simple SMTP mail transfers in an SMTP client application to an SMTP mail
   server.   */

#include "nx_api.h"
#include "nx_ip.h"
#include "nxd_smtp_client.h"


/* Define the host user name and mail box parameters */
#define USERNAME               "myusername"
#define PASSWORD               "mypassword"
#define FROM_ADDRESS           "my@mycompany.com"
#define RECIPIENT_ADDRESS      "your@yourcompany.com"
#define LOCAL_DOMAIN           "mycompany.com"

#define SUBJECT_LINE           "NetX Duo SMTP Client Demo"
#define MAIL_BODY              "NetX Duo SMTP client is an SMTP client \r\n" \
                               “implementation for embedded devices to send  \r\n" \
                               "email to SMTP servers. This feature is \r\n" \
                               "intended to allow a device to send simple \r\n " \
                               "status reports using the most universal \r\n " \
                               “Internet application, email.\r\n"

/* See the NetX Duo SMTP Client User Guide for how to set the authentication type.
   The most common authentication type is PLAIN. */
#define CLIENT_AUTHENTICATION_TYPE 3


#define CLIENT_IP_ADDRESS  IP_ADDRESS(1,2,3,5)
#define SERVER_IP_ADDRESS  IP_ADDRESS(1,2,3,4)
#define SERVER_PORT        25


/* Define the NetX Duo and ThreadX structures for the SMTP client appliciation. */
NX_PACKET_POOL                  ip_packet_pool;
NX_PACKET_POOL                  client_packet_pool;
NX_IP                           client_ip;
TX_THREAD                       demo_client_thread;
static NX_SMTP_CLIENT           demo_client;


void    _nx_ram_network_driver(struct NX_IP_DRIVER_STRUCT *driver_req);
void    demo_client_thread_entry(ULONG info);

/* 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    *free_memory_pointer;


    /* Setup the pointer to unallocated memory.  */
    free_memory_pointer =  (CHAR *) first_unused_memory;

    /* Create IP default packet pool. */
    status =  nx_packet_pool_create(&ip_packet_pool, "Default IP Packet Pool",
                                    128, free_memory_pointer, 2048);

    /* Update pointer to unallocated (free) memory. */
    free_memory_pointer = free_memory_pointer + 2048;

    /* Create SMTP Client packet pool. This is only for transmitting packets to the
       server. It need not be a separate packet pool than the IP default packet pool
       but for more efficient resource use, we use two different packet pools
       because the CLient SMTP messages generally require more payload than IP
       control packets.

       Packet payload depends on the SMTP Client application requirements.  Size of
       packet payload must include IP and TCP headers. For IPv6 connections, IP and
       TCP header data is 60 bytes. For IPv4 IP and TCP header data is 40 bytes (not
       including TCP options). */
    status |=  nx_packet_pool_create(&client_packet_pool, "SMTP Client Packet Pool",
                                     800, free_memory_pointer, (10*800));

    if (status != NX_SUCCESS)
    {
        return;
    }

    /* Update pointer to unallocated (free) memory. */
    free_memory_pointer = free_memory_pointer + (10*800);

    /* Initialize the NetX system. */
    nx_system_initialize();

    /* Create the client thread */
    status = tx_thread_create(&demo_client_thread, "client_thread",
                              demo_client_thread_entry, 0, free_memory_pointer,
                              2048, 16, 16,
                              TX_NO_TIME_SLICE, TX_DONT_START);

    if (status != NX_SUCCESS)
    {

        printf("Error creating Client thread. Status 0x%x\r\n", status);
        return;
    }

    /* Update pointer to unallocated (free) memory. */
    free_memory_pointer =  free_memory_pointer + 4096;


    /* Create Client IP instance. Remember to replace the generic driver
       with a real ethernet driver to actually run this demo! */

    status = nx_ip_create(&client_ip, "SMTP Client IP Instance", CLIENT_IP_ADDRESS,
                          0xFFFFFF00UL, &ip_packet_pool, _nx_ram_network_driver,
                          free_memory_pointer, 2048, 1);


    free_memory_pointer =  free_memory_pointer + 2048;

    /* Enable ARP and supply ARP cache memory. */
    status =  nx_arp_enable(&client_ip, (void **) free_memory_pointer, 1040);

    /* Update pointer to unallocated (free) memory. */
    free_memory_pointer = free_memory_pointer + 1040;

    /* Enable TCP for client. */
    status =  nx_tcp_enable(&client_ip);

    if (status != NX_SUCCESS)
    {
        return;
    }

    /* Enable ICMP for client. */
    status =  nx_icmp_enable(&client_ip);

    if (status != NX_SUCCESS)
    {
        return;
    }

    /* Start the client thread. */
    tx_thread_resume(&demo_client_thread);

    return;
}


/* Define the smtp application thread task.   */
void    demo_client_thread_entry(ULONG info)
{

    UINT        status;
    UINT        error_counter = 0;
    NXD_ADDRESS server_ip_address;


    tx_thread_sleep(100);

    /* Set up the server IP address. */
    server_ip_address.nxd_ip_version = NX_IP_VERSION_V4;
    server_ip_address.nxd_ip_address.v4 = SERVER_IP_ADDRESS;

    /* The demo client username and password is the authentication
       data used when the server attempts to authentication the client. */

    status =  nxd_smtp_client_create(&demo_client, &client_ip, &client_packet_pool,
                                     USERNAME,
                                     PASSWORD,
                                     FROM_ADDRESS,
                                     LOCAL_DOMAIN, CLIENT_AUTHENTICATION_TYPE,
                                     &server_ip_address, SERVER_PORT);

    if (status != NX_SUCCESS)
    {
        printf("Error creating the client. Status: 0x%x.\n\r", status);
        return;
    }

    /* Create a mail instance with the above text message and recipient info. */
    status =  nx_smtp_mail_send(&demo_client, RECIPIENT_ADDRESS,
                                TP_MAIL_PRIORITY_NORMAL,
                                SUBJECT_LINE, MAIL_BODY, sizeof(MAIL_BODY) - 1);

    /* Check for errors. */
    if (status != NX_SUCCESS)
    {

        /* Mail item was not sent. Note that we need not delete the client. The
           error status may be a failed authentication check or a broken connection.
           We can simply call nx_smtp_mail_send again.  */
        error_counter++;
    }

    /* Release resources used by client. Note that the transmit packet
       pool must be deleted by the application if it no longer has use for it.*/
    status = nx_smtp_client_delete(&demo_client);

    /* Check for errors. */
    if (status != NX_SUCCESS)
    {
        error_counter++;
    }

    return;
}

图 1. SMTP 客户端与 NetX Duo 结合使用的示例

客户端配置选项

NetX Duo SMTP 客户端 API 有多个配置选项。 下面提供了所有选项的列表及详细说明:

  • NX_SMTP_CLIENT_TCP_WINDOW_SIZE:此选项设置客户端 TCP 接收窗口的大小。 应将其设置为低于基础以太网硬件的 MTU 大小,并为 IP 和 TCP 标头留出空间。 默认的 NetX Duo SMTP 客户端 TCP 窗口大小为 1460。
  • NX_SMTP_CLIENT_PACKET_TIMEOUT:此选项设置分配 NetX 数据包时的超时时间。 默认的 NetX Duo SMTP 客户端数据包超时时间为 2 秒。
  • NX_SMTP_CLIENT_CONNECTION_TIMEOUT:此选项设置客户端 TCP 套接字连接超时时间。 默认的 NetX Duo SMTP 客户端连接超时时间为 10 秒。
  • NX_SMTP_CLIENT_DISCONNECT_TIMEOUT:此选项设置客户端 TCP 套接字连接断开超时时间。 默认的 NetX Duo SMTP 客户端连接断开超时时间为 5 秒*。 注意,如果 SMTP 客户端遇到内部错误(例如连接中断),则可能会终止连接,而不会等待超时。
  • NX_SMTP_GREETING_TIMEOUT:此选项设置客户端接收服务器对其问候信息的回复时的超时时间。 默认的 NetX Duo SMTP 客户端值为 10 秒。
  • NX_SMTP_ENVELOPE_TIMEOUT:此选项设置客户端接收服务器对客户端命令的回复时的超时时间。 默认的 NetX Duo SMTP 客户端值为 10 秒。
  • NX_SMTP_MESSAGE_TIMEOUT:此选项设置客户端接收服务器的邮件数据接收回复时的超时时间。 默认的 NetX Duo SMTP 客户端值为 30 秒。
  • NX_SMTP_CLIENT_SEND_TIMEOUT:此选项定义在向服务器进行 SMTP 身份验证期间,缓冲区存储用户密码时的等待选项。 默认值为 20 个字节。
  • NX_SMTP_SERVER_CHALLENGE_MAX_STRING:此选项定义在 SMTP 身份验证期间提取服务器质询的缓冲区的大小。 默认值为 200 个字节。 对于 LOGIN 和 PLAIN 身份验证,SMTP 客户端可能会使用较小的缓冲区。
  • NX_SMTP_CLIENT_MAX_PASSWORD:此选项定义在向服务器进行 SMTP 身份验证期间存储用户密码的缓冲区的大小。 默认值为 20 个字节。
  • NX_SMTP_CLIENT_MAX_USERNAME:此选项定义在向服务器进行 SMTP 身份验证期间存储主机用户名的缓冲区的大小。 默认值为 40 个字节。