c++ 实现邮件发送功能

系列服务器开发


前言

在这里插入图片描述
常用的电子邮件协议有SMTP、POP3、IMAP4,它们都隶属于TCP/IP协议簇,默认状态下,分别通过TCP端口25、110和143建立连接。
SMTP的全称是"SimpleMailTransferProtocol",即简单邮件传输协议
POP全称是邮局协议,负责从邮件服务器中检索电子邮件。
IMAP全称互联网信息访问协议,是一种优于POP的新协议。


一、SMTP是什么?

SMTP是一种提供可靠且有效的电子邮件传输的协议。SMTP是建立在FTP文件传输服务上的一种邮件服务,主要用于系统之间的邮件信息传递,并提供有关来信的通知。SMTP独立于特定的传输子系统,且只需要可靠有序的数据流信道支持,SMTP的重要特性之一是其能跨越网络传输邮件,即“SMTP邮件中继”。使用SMTP,可实现相同网络处理进程之间的邮件传输,也可通过中继器或网关实现某处理进程与其他网络之间的邮件传输。

SMTP是一个相对简单的基于文本的协议。在其之上指定了一条消息的一个或多个接收者(在大多数情况下被确认是存在的),然后消息文本会被传输。可以很简单地通过telnet程序来测试一个SMTP服务器。SMTP使用TCP端口25。Sendmail是最早使用SMTP的邮件传输代理之一。

SMTP协议工作机制
虽然SMTP涉及的是发送电子邮件,但是整理和传送邮件的工作是由不同的服务器完成的,称为:POP(邮局协议)和IMAP(Internet消息访问协议)。通常它工作在两种情况下:一是邮件从客户机传输到服务器;二是从某一个服务器传输到另一个服务器。

SMTP的连接和发送过程
(a)建立TCP连接
(b)客户端发送HELLO命令以标识发件人自己的身份,然后客户端发送MAIL命令;服务器端以OK作为响应,表明准备接收
(c)客户端发送RCPT命令,以标识该电子邮件的计划接收人,可以有多个RCPT行;服务器端则表示是否愿意为收件人接收邮件
(d)协商结束,发送邮件,用命令DATA发送
(e)以.表示结束输入内容一起发送出去
(f)结束此次发送,用QUIT命令退出

二、使用SMTP使用步骤

本文基于sendemail可执行程序,通过进程调用来实现的邮件发送。

sendEmail发送邮件
sendEmail是一个轻量级,命令行的SMTP邮件客户端。如果你需要使用命令行发送邮件,那么sendEmail是非常完美的选择:使用简单并且功能强大。

1.下载编译,命令行使用

sendEmail 官网:http://caspian.dotconf.net/menu/Software/SendEmail/
sendEmail 下载:sendEmail-v1.56.tar.gz

1、sendEmail 下载
sendEmail-v1.56.tar.gz

2、解压、安装
tar zxvf sendEmail-v1.56.tar.gz
cd sendEmail-v1.56
cp sendEmail /usr/local/bin/


可以通过查看帮助
sendEmail --help

简单发送邮件案例
sendEmail -f ttlsafrom@163.com -t 12312311231@qq.com -s smtp.163.com -u "主题测试" -o message-content-type=html -o message-charset=utf-8 -o tls=no -xu ttlsafrom@163.com -xp HUKINXIAW -m "test"

Feb 15 09:22:15 localhost sendEmail[3536]: Email was sent successfully!
参数解释:
sendEmail      主程序
-f ttlsafrom@163.com          发件人邮箱
-s smtp.163.com               发件人邮箱的smtp服务器
-u "我是邮件主题"                邮件的标题
-o message-content-type=html   邮件内容的格式,html表示它是html格式
-o message-charset=utf8        邮件内容编码
-xu ttlsafrom@163.com          发件人邮箱的用户名
-xp HUKINXIAW                  发件人邮箱密码
-m "我是邮件内容"                邮件的具体内容
-t 12312311231@163.com         收件箱
-cc                            表示抄送
-bcc                           表示暗抄送

如何获取客户端授权码即xp后面的参数:
1)在web端登录邮箱后,进入设置—>邮箱设置—>POP3/SMTP/IMAP设置页面
如果您还未开启授权码,请了解开启步骤。请参考(https://note.youdao.com/ynoteshare/index.html?id=f9fef46114fb922b45460f4f55d96853&type=note&_time=1664544625625)
如果您已经开启过授权码,但是忘记了之前的授权码是多少。
那您可以新增一个授权码,并且不会影响之前已经设置好的客户端。(最多可以新增5个授权码,任意一个授权码都可以登录使用任意客户端)
2)点击新增授权码,按照提示扫码发送短信,或者手动发送短信即可。然后就可以看到您新的授权码了。

2.代码实现


#pragma once
#include <string>

class MailParam
{
public:
     std::string _from_name;
     std::string _from;
     std::string _to_name1;
     std::string _to_email1;
     std::string _server;
     std::string _server_token;     
};
class MailImpl
{
public:    
     MailImpl();
     ~MailImpl();
     void init(MailParam &param);      
     bool sendMailByExe(const std::string &title,const std::string &content);
private:
     std::string _from_name;
     std::string _from;
     std::string _to_name1;
     std::string _to_email1;
     std::string _server;
     std::string _server_token;
private:
     MailImpl(const MailImpl&)=delete;
     MailImpl& operator=(const MailImpl&)=delete;   
};


#include "sendmail.h"
#include <iostream>

MailImpl::MailImpl()
{
}

MailImpl::~MailImpl()
{
}

void MailImpl::init(MailParam &param) 
{
    _from_name = param._from_name;
    _from = param._from;
    _to_name1 = param._to_name1;
    _to_email1 = param._to_email1;
    _server = param._server;
    _server_token = param._server_token;     
}


bool MailImpl::sendMailByExe(const std::string &title,const std::string &content)
{
    bool result = true;
    do{
        const auto from_name = _from_name;
        const auto from =  _from;
        const auto to_name1 =_to_name1;
        const auto to_email1 = _to_email1;
        const auto server = _server;
        const auto server_token = _server_token;
        
        //check if configured
        if(from_name.length() ==0 ||
            from.length() ==0 ||
            server.length() ==0 ||
            server_token.length() ==0
        ){
            std::cout<< "email configure no has congiure info"<<std::endl;
            result = false;
            break;             
        }
        if( (to_name1.length() ==0 || to_email1.length() == 0) ){
            std::cout<< "email configure no has receiver"<<std::endl;
            result = false;
            break;             
        }

        //./sendEmail -f ******@163.com -t ***@qq.com 
        //-s smtp.163.com -u "new msg need to notify" 
        //-o message-content-type=html -o message-charset=utf-8 -o tls=no 
        //-xu *****@163.com -xp HUKINXIAWCHAHARJ -m "test"
        //-a /opt/vplatform/attachment.txt 
        
        char cmd[1024] = {0};
        std::string path =  "/opt/app/sendEmail";
        std::string subject = title;
        std::string format = "-o message-content-type=html -o message-charset=utf-8 -o tls=no";
        
        int ret = 0;
        if(to_name1.length()>0 && to_email1.length()>0){
            std::string body = from_name + ":" + to_name1 + "," + content;
            sprintf(cmd, "%s -f %s -t %s -s %s -u %s %s -xu %s -xp %s -m %s"
            ,path.c_str(),from.c_str(),to_email1.c_str()
            ,server.c_str(),subject.c_str(),format.c_str()
            ,from.c_str(),server_token.c_str(),body.c_str());
            ret = system(cmd);
            std::cout<< "call email send result "<< ret << std::endl;
        }
        if(ret != 0){
            result = false;
            break;
        }
    }while(false);

    return result;
}


总结

通过本文的学习,应该多smtp协议发送邮件有个大致的了解,本文是基于c++实现的。