第十一章 W55MH32 SMTP示例

目录

[1 SMTP协议简介](#1 SMTP协议简介)

[2 SMTP协议特点](#2 SMTP协议特点)

[3 SMTP应用场景](#3 SMTP应用场景)

[4 SMTP发送邮件流程](#4 SMTP发送邮件流程)

[5 STMP协议的主要命令](#5 STMP协议的主要命令)

[6 实现过程](#6 实现过程)

[7 运行结果](#7 运行结果)

[8 总结](#8 总结)


本篇文章,我们将详细介绍如何在W55MH32芯片上面实现SMTP协议。并通过实战例程,为大家讲解如何在W55MH32上使用SMTP协议给他人发送电子邮件。

该例程用到的其他网络协议,例如 DHCP和DNS,请参考相关章节。有关 W55MH32 的初始化过程,请参考 Network Install 章节,这里将不再赘述。

1 SMTP协议简介

SMTP(Simple Mail Transfer Protocol,简单邮件传输协议) 是一种用于电子邮件传输的通信协议。它是互联网标准协议之一,专门设计用于电子邮件的发送和路由。SMTP定义了邮件如何从发件人发送到收件人的电子邮件服务器,并规范了服务器之间的邮件中继操作。

2 SMTP协议特点

面向文本 :SMTP协议使用纯文本命令和响应(如HELO、MAIL FROM、RCPT TO等)来进行通信,邮件内容通常是以ASCII码表示。

请求- 响应模式 :SMTP通信是基于请求-响应模型的,客户端发送请求,服务器根据请求返回响应。

基于TCP :SMTP依赖于TCP协议来提供可靠的数据传输服务。SMTP会通过TCP建立连接并发送邮件。

广泛兼容性 :SMTP 是国际标准邮件传输协议,广泛应用于邮件系统,确保不同系统间高效互通。

高效性与可靠性 :SMTP 协议简单易用,支持错误处理和重试机制。邮件无法发送时,可暂存队列并重试,确保传输可靠。

可扩展性 :通过扩展SMTP协议的命令和响应码,可以支持更多的邮件传输特性和功能。

安全性 :SMTP 本身不处理加密,但可结合 SSL/TLS(SMTPS)提供加密通道,保障邮件传输的安全性。

异步传输 :SMTP 支持异步传输,使邮件发送和接收可在不同时间进行,提升效率并支持批量处理。

灵活性 :SMTP 设计灵活,可配置邮件路由、优先级、大小限制等,以满足不同需求。

3 SMTP应用场景

接下来,我们了解下在W55MH32上,可以使用SMTP协议完成哪些操作及应用呢?

  1. 物联网(IoT )设备远程监控 :使用W55MH32实现SMTP通信,收集传感器数据,并发送定期报告或警报邮件。
  2. 环境监测系统 :W55MH32通过SMTP协议将监测数据(如温湿度、CO2浓度等)发送到指定的邮箱,确保及时获取信息。
  3. 设备状态报告与日志记录 :通过定时任务触发W55MH32每隔一段时间自动发送设备的状态报告或运行日志到管理人员邮箱。
  4. 远程故障报警与支持 :结合传感器和W55MH32的邮件发送功能,可以实现自动报警,减少人工干预的需要。
  5. 工业自动化和远程监控 :使用W55MH32连接到互联网,并利用SMTP协议将设备状态、报警等信息发送到指定的邮箱。
  6. 远程控制反馈 :通过W55MH32接收邮件,解析邮件命令并执行相应操作,然后通过SMTP将执行结果反馈给发送者。

4 SMTP发送邮件流程

  1. 使用TCP协议连接SMTP服务器
  2. 发送握手消息
  3. 发送用户认证消息
  4. 设置邮件发送地址
  5. 传输邮件内容
  6. 完成邮件发送

5 STMP协议的主要命令

SMTP服务器响应状态码

6 实现过程

接下来,我们在W55MH32上实现SMTP邮件发送功能。

注意:因为本示例需要访问互联网,请确保 W55MH32 的配置能够访问互联网。

步骤一:SMTP 发送内容初始化

发送指令定义:

cpp 复制代码
char    hello[50]      = "HELO localhost";                   // Identity command
char    hello_reply[] = "250 OK";                           // Id successfully responded
char    AUTH[50]      = "AUTH LOGIN";                       // Authentication request
char    AUTH_reply[]  = "334 dXNlcm5hbWU6";                 // The authentication request was successfully sent
char    name_126[100] = "wiznethk@126.com";                 // 126 Login email address
char    base64name_126[200];                                // 126 base64 encoding of the login mailbox name
char    name_reply[]     = "334 UGFzc3dvcmQ6";              // The login name was sent successfully
char    password_126[50] = "ZPURADLGRUPQLVBK";              // 126 Email login password
char    base64password_126[100];                            // base64 123 Password for logging in to the mailbox
char    password_reply[] = "235 Authentication successful"; // Login successful response
char    from[]           = "wiznethk@126.com";              // Sender email
char    from_reply[]     = "250 Mail OK";
char    to[]             = "2510582273@qq.com";             // Recipient email address
char    to_reply[]       = "250 Mail OK";
char    data_init[10]    = "data";                          // Request data transfer
char    data_reply[]     = "354";                           // The request was successfully responded to HEAD
char    Cc[]             = "";                              // Cc to email
char    subject[]        = "Hello!WIZnet!";                 // subject
char    content[]        = "Hello!WIZnet!";                 // text part
char    mime_reply[]     = "250 Mail OK queued as";         // The email was sent successfully
char    mailfrom[50]     = "MAIL FROM:<>";
char    rcptto[50]       = "rcpt to:<>";
char    mime[200]        = "From:\r\n";
char    mime1[50]        = "To:\r\n";
char    mime2[50]        = "Cc:\r\n";
char    mime3[50]        = "Subject:\r\n";
char    mime4[50]        = "MIME-Version:1.0\r\nContent-Type:text/plain\r\n\r\n";
char    mime5[50]        = "\r\n.\r\n";

步骤二:发送邮件内容初始化:

cpp 复制代码
    mailmessage(); // Mail command information processing

mailmessage()函数内容如下:

cpp 复制代码
void mailmessage(void)
{
    uint16_t len_from = strlen(from);
    uint16_t len_to   = strlen(to);
    uint16_t len_Cc   = strlen(Cc);
    uint16_t len_sub  = strlen(subject);
    strcat(hello, "\r\n");
    strcat(AUTH, "\r\n");
    base64encode(name_126, base64name_126);
    base64encode(password_126, base64password_126);
    strcat(base64name_126, "\r\n");
    strcat(base64password_126, "\r\n");
    str_insert(mailfrom, from, 11);
    strcat(mailfrom, "\r\n");
    str_insert(rcptto, to, 9);
    strcat(rcptto, "\r\n");
    strcat(data_init, "\r\n");

    str_insert(mime, from, 5);
    str_insert(mime1, to, 3);
    str_insert(mime2, Cc, 3);
    str_insert(mime3, subject, 8);
    str_insert(mime5, content, 0);
    strcat(mime, mime1);
    strcat(mime, mime2);
    strcat(mime, mime3);
    strcat(mime, mime4);
    strcat(mime, mime5);
}

步骤三:使用DNS协议解析SMTP 服务器地址

cpp 复制代码
    if (do_dns(ethernet_buf, smtp_server_name, smtp_server_ip))
    {
        while (1)
        {
        }
    }

步骤四:SMTP发送邮件操作

cpp 复制代码
    while (1)
    {
        do_smtp(SOCKET_ID, ethernet_buf, smtp_server_ip); // smtp run
    }

do_smtp()函数内容如下:

cpp 复制代码
void do_smtp(uint8_t sn, uint8_t *buf, uint8_t *smtp_server_ip)
{
    volatile uint8_t ret;
    uint32_t         len       = 0;
    uint16_t         anyport   = 5000;
    uint8_t          Smtp_PORT = 25;
    memset(buf, 0, ETHERNET_MAX_BUF_SIZE);
    switch (getSn_SR(sn))
    {
    case SOCK_INIT:
        ret = connect(sn, smtp_server_ip, Smtp_PORT);
        break;
    case SOCK_ESTABLISHED:
        if (getSn_IR(sn) & Sn_IR_CON)
        {
            setSn_IR(sn, Sn_IR_CON);
        }

        while (!Mail_Send_OK)
        {
            len = getSn_RX_RSR(sn);
            if (len > 0)
            {
                memset(buf, 0, ETHERNET_MAX_BUF_SIZE);
                len = recv(sn, (uint8_t *)buf, len);
                send_mail(sn, buf, smtp_server_ip);
            }
        }
        disconnect(sn);
        break;
    case SOCK_CLOSE_WAIT:
        if ((len = getSn_RX_RSR(sn)) > 0)
        {
            while (!Mail_Send_OK)
            {
                len = recv(sn, (uint8_t *)buf, len);
                send_mail(sn, buf, smtp_server_ip);
            }
        }
        disconnect(sn);
        break;
    case SOCK_CLOSED:
        socket(sn, Sn_MR_TCP, anyport++, 0x00);
        break;
    default:
        break;
    }
    if (Mail_Send_OK)
    {
        while (1)
        {
        }
    }
}

在该函数中,程序会执行一个 TCP Client 模式的状态机,详细讲解请参考TCP Client章节,这里不再赘述。当程序处于 SOCK_ESTABLISHED (即成功连接上SMTP服务器)状态时,SMTP服务器会主动发送一条消息给W55MH32,当接收到这条消息后,进入send_mail()函数进行SMTP发送邮件流程:

send_mail()函数内容如下:

cpp 复制代码
void send_mail(uint8_t sn, uint8_t *buf, uint8_t *smtp_server_ip)
{
    volatile uint8_t ret;
    switch (SMTP_STATE)
    {
    case waitfor220:
        if (strstr((const char *)buf, "220") != NULL)
        {
            ret        = send(sn, (uint8_t *)hello, strlen(hello));
            SMTP_STATE = waitforHELO250;
        }
        else
        {
            printf("Connected failed!\r\n");
        }
        break;
    case waitforHELO250:
        if (strstr((const char *)buf, hello_reply) != NULL && strstr((const char *)buf, "Mail") == NULL)
        {
            ret        = send(sn, (uint8_t *)AUTH, strlen(AUTH));
            SMTP_STATE = waitforAUTH334;
        }
        else
        {
            printf("smtp handshake failed!\r\n");
        }
        break;
    case waitforAUTH334:
        if (strstr((const char *)buf, AUTH_reply) != NULL)
        {
            ret        = send(sn, (uint8_t *)base64name_126, strlen(base64name_126));
            SMTP_STATE = waitforuser334;
        }
        else
        {
            printf("AUTH authentication request failed!\r\n");
        }
        break;
    case waitforuser334:
        if (strstr((const char *)buf, name_reply) != NULL)
        {
            ret        = send(sn, (uint8_t *)base64password_126, strlen(base64password_126));
            SMTP_STATE = waitforpassword235;
        }
        else
        {
            printf("username send failed!\r\n");
        }
        break;
    case waitforpassword235:
        if (strstr((const char *)buf, password_reply) != NULL)
        {
            ret        = send(sn, (uint8_t *)mailfrom, strlen(mailfrom));
            SMTP_STATE = waitforsend250;
        }
        else
        {
            printf("password error!\r\n");
        }
        break;
    case waitforsend250:
        if (strstr((const char *)buf, from_reply) != NULL && strstr((const char *)buf, "queued as") == NULL)
        {
            ret        = send(sn, (uint8_t *)rcptto, strlen(rcptto));
            SMTP_STATE = waitforrcpt250;
        }
        else
        {
            printf("Send email failed to set up!\r\n");
        }
        break;
    case waitforrcpt250:
        if (strstr((const char *)buf, to_reply) != NULL)
        {
            ret        = send(sn, (uint8_t *)data_init, strlen(data_init));
            SMTP_STATE = waitfordate354;
        }
        else
        {
            printf("Failed to set the receiving mailbox!\r\n");
        }
        break;
    case waitfordate354:
        if (strstr((const char *)buf, data_reply) != NULL)
        {
            ret        = send(sn, (uint8_t *)mime, strlen(mime));
            SMTP_STATE = waitformime250;
        }
        else
        {
            printf("Failed to send content setup\r\n");
        }
        break;
    case waitformime250:
        if (strstr((const char *)buf, mime_reply) != NULL)
        {
            Mail_Send_OK = 1;
            printf("mail send OK\r\n");
        }
        break;
    default:
        break;
    }
}

send_mail()中,会执行一个发送邮件的状态机,流程如下图所示:

1.SMTP 握手:发送 HELO命令,等待服务器返回 250 OK 响应码,确认握手成功。

2. 用户认证:

(1) 请求认证:发送 AUTH LOGIN 命令,表明客户端需要认证,等待服务器返回 334 ,请求用户名。

(2) 提交用户名:发送经过Base64编码的用户名(如:base64name_126),等待服务器返回 334 ,请求密码。

(3) 提交密码:发送经过Base64编码的密码(如:base64password_126),等待服务器返回 235 ,确认认证成功。

3**.** 设置邮件发送地址:

(1) 设置发件人:发送 MAIL FROM:<发件人邮箱>,等待服务器返回 250 ,确认发件人地址成功设置。

(2) 设置收件人:发送 RCPT TO:<收件人邮箱>,等待服务器返回 250,确认收件人地址成功设置。

4. 传输邮件内容:

(1) 请求数据传输:发送 DATA 命令,表明开始传输邮件数据,等待服务器返回 354,表示已准备接收数据。

(2) 发送邮件数据:按以下格式发送邮件数据:

发件人信息:From:

收件人信息:To:

抄送信息:Cc:

邮件主题:Subject:

邮件内容:实际的文本部分

数据结束用 \r\n.\r\n 表示

等待服务器返回 250 ,确认邮件内容已成功排队

5. 完成邮件发送:

如果服务器返回 250 ,标志邮件成功发送。

设置 Mail_Send_OK = 1,并打印提示信息 mail send OK

7 运行结果

烧录例程运行后,首先进行了PHY链路检测,接着是通过DHCP获取网络地址信息,然后是通过DNS解析SMTP服务器域名,最后是进行邮件发送,如下图所示:

可以在设置接收邮件的账号中查找接收到的邮件:

通过wireshark抓包查看,流程与send_mail()函数一致。

8 总结

本文讲解了如何在 W55MH32 芯片上实现 SMTP 协议,通过实例详细展示了在该芯片上使用 SMTP 协议发送电子邮件的实现流程,包括 SMTP 发送内容初始化、使用 DNS 协议解析 SMTP 服务器地址、SMTP 发送邮件操作等核心步骤。文章还对 SMTP 协议的简介、特点、应用场景,以及主要命令和服务器响应状态码进行了分析,帮助读者理解其在邮件传输中的实际应用价值。

下一篇文章将介绍NetBIOS的原理及在网络通信中的应用,同时讲解如何在W55MH32芯片上实现NetBIOS功能,敬请期待!

相关推荐
凉、介12 小时前
ARM 学习笔记(四)
c语言·arm开发·笔记·学习·嵌入式
WIZnet13 小时前
第二十七章 W55MH32 Interrupt示例
物联网·以太网·wiznet·中断·高性能以太网单片机·w55mh32·toe
凉、介2 天前
ARM 学习笔记(三)
arm开发·笔记·学习·嵌入式
亿道电子Emdoor2 天前
【ARM】当选择AC5时每次点击build都会全编译
arm开发·arm
九鼎创展科技2 天前
九鼎X8390 开发板 & 联发科 MT8390 / MT8370 芯片平台
android·arm开发·嵌入式硬件·硬件工程
华清远见成都中心2 天前
ARM入门学习方法分享
arm开发·学习方法
亿道电子Emdoor2 天前
[ARM]MDK出现报错error: A\L3903U的解决方法
arm开发·stm32·单片机·arm
菜鸟12号3 天前
Linux下SPI设备驱动开发
linux·arm开发·驱动开发
@陽光總在風雨後5 天前
嵌入式数据结构之顺序表总结
c语言·arm开发·stm32·单片机·嵌入式硬件