第十一章 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功能,敬请期待!

相关推荐
AI+程序员在路上13 分钟前
Nand Flash与EMMC区别及ARM开发板中的应用对比
arm开发
“αβ”1 小时前
数据链路层协议 -- 以太网协议与ARP协议
服务器·网络·网络协议·以太网·数据链路层·arp·mac地址
17(无规则自律)6 小时前
深入浅出 Linux 内核模块,写一个内核版的 Hello World
linux·arm开发·嵌入式硬件
梁洪飞19 小时前
内核的schedule和SMP多核处理器启动协议
linux·arm开发·嵌入式硬件·arm
代码游侠1 天前
学习笔记——Linux字符设备驱动
linux·运维·arm开发·嵌入式硬件·学习·架构
syseptember2 天前
Linux网络基础
linux·网络·arm开发
代码游侠2 天前
学习笔记——Linux字符设备驱动开发
linux·arm开发·驱动开发·单片机·嵌入式硬件·学习·算法
程序猿阿伟2 天前
《Apple Silicon与Windows on ARM:引擎原生构建与模拟层底层运作深度解析》
arm开发·windows
wkm9562 天前
在arm64 ubuntu系统安装Qt后编译时找不到Qt3DExtras头文件
开发语言·arm开发·qt
unicrom_深圳市由你创科技2 天前
基于ARM+DSP+FPGA异构计算架构的高速ADC采集卡定制方案
arm开发·fpga开发