• 【案例】| C++实现STMP发送邮件


    一、STMP基础知识

    1、简介

    - SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议,它是一组用于由源地址到目的地址传送邮件的规则,由它来控
    	制信件的中转方式;
    - SMTP协议属于TCP/IP协议族,它帮助每台计算机在发送或中转信件时找到下一个目的地,通过SMTP协议所指定的服务器,就可以
    	把E-mail寄到收信人的服务器上了,整个过程只要几分钟;而SMTP服务器则是遵循SMTP协议的发送邮件服务器,用来发送或中
    	转发出的电子邮件;
    - MIME规范使得二进制文件能够通过SMTP来传输。目前大多数SMTP软件实现都支持8位MIME扩展,从而使二进制文件的传输变得
    	几乎和纯文本一样简单。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2、工作流程

    在这里插入图片描述

    - 当SMTP客户端有邮件要传送时,与SMTP服务器建立一个双向的传输通道;SMTP客户端负责邮件信息传送到一个或多个SMTP服
    	务器,如果失败则给出报告;
    - SMTP服务器可能最终目的地,也可能是中间的中继或者网关。SMTP命令由SMTP客户端产生,发送到SMTP服务器。SMTP响应
    	由SMTP服务器发送给SMTP客户端,对命令做出回应;
    - 邮件传输者可以出现在起始SMTP发送方与最终的SMTP接收方之间建立的连接上,或者出现在通过中间系统的一系列跃点上。一
    	旦传输通道建立和初始握手完成,SMTP客户端正常初始化邮件事务。这样的事务包括一系列命令,以定义邮件的发送方和目的
    	地,以邮件内容本身的传递;
    - 发送者发送 MAIL 命令来指定发送者的邮件,如果接受者接收这个邮件,就回复OK ,接着发送者发送 RCPT命令来指定接收者的
    	邮箱,如果被接收同样回复OK,如果不接受则拒绝(不会终止整个通话)。接收者邮箱确定后,发送者用DATA命令指示要发送
    	数据,并用一个 .  结束发送。如果数据被接收,会收到OK ,然后用QUIT结束会话;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    3、常用指令

    【EHLO】:参数为SMTP客户端全称域名;与服务器确认通知其客户端使用的机器名称和地址,标识身份;
    	客户端发出后,若服务器支持SMTP服务,则会给出相应的回应;
    【AUTH】:使用AUTH LOGIN与服务器进行登录验证;
    【MAIL FROM】:发件人信息,若与认证信息不同则被定位为垃圾/恶意邮件;
    【RCPT TO】:收件人地址;
    【DATA】:邮件基本信息;
    【FROM】:邮件基本信息;
    【SUBJECT】:邮件标题;
    【QUIT】:断开连接;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    4、SMTP响应码

    211System status, or system help reply
    214Help message
    220<domain> Service ready
    221<domain> Service closing transmission channel
    235Authentication successful
    250Requested mail action okay, completed
    251User not local; will forward to <forward-path>
    252Cannot VRFY user, but will accept message and attempt delivery
    334AUTH input
    354Start mail input; end with <CRLF>.<CRLF>
    421<domain> Service not available, closing transmission channel
    432A password transition is needed
    450Requested mail action not taken: mailbox unavailable
    451Requested action aborted: local error in processing
    452Requested action not taken: insufficient system storage"
    454Temporary authentication failed
    500Syntax error, command unrecognized
    501Syntax error in parameters or arguments
    502Command not implemented
    503Bad sequence of commands
    504Command parameter not implemented
    530Authentication required
    534Authentication mechanism is too weak
    535Authentication credentials invalid
    538Encryption required for requested authentication mechanism
    550Requested action not taken: mailbox unavailable
    551User not local; please try <forward-path>
    552Requested mail action aborted: exceeded storage allocation
    553Requested action not taken: mailbox name not allowed
    554Transaction failed

    二、代码

    添加链接描述

    #include "smtp.h"
    #include <iostream>
    
    namespace Jxiepc {
    
    
    Smtp::Smtp(int port, std::string domain, std::string user, std::string pwd, 
            std::string t_mail, std::string title, std::string content, std::string type)
        : m_port(port), m_domain(domain), m_user(user), m_password(pwd), m_tmail(t_mail),
        m_title(title), m_content(content), m_type(type) {
    
        if(init() < 0) {
            perror("init error");
        }
    }
    
    Smtp::~Smtp() {
        close(m_sockfd);    
    }
    
    int Smtp::init() {
        if(make_connect() == -1) {
            return -1;
        }
        std::string str;
    
        Recv();
        if(strstr(m_buf, "220") == nullptr) { return -1; } 
        std::cout << "****: " << m_buf << std::endl;
    
        Send("HELO " + m_user + "\r\n");
        Recv();
        if(strstr(m_buf, "250") == nullptr) { return -1; }
    
    
        Send("AUTH LOGIN\r\n");
        Recv();
        if(strstr(m_buf, "334") == nullptr) { return -1; }
    
        str = m_user.substr(0, m_user.find('@', 0));
        str = enBase64(str.c_str());
        str += "\r\n";
        Send(str);
        Recv();
        if(strstr(m_buf, "334") == nullptr) { return -1; }
    
        Send(enBase64(m_password.c_str()) + "\r\n");
        Recv();
        if(strstr(m_buf, "235") == nullptr) { return -1; }
        std::cout << "AUTH SUCCESS..." << std::endl;
    
        Send("MAIL FROM: <" + m_user + ">\r\n");
        Recv();
        if(strstr(m_buf, "250") == nullptr) { return -1; }
    
        Send("RCPT TO: <" + m_tmail+ ">\r\n");
        Recv();
        if(strstr(m_buf, "250") == nullptr) { return -1; }
    
        Send("DATA\r\n");
        Recv();
    
        str = "From: " + m_user + "\r\n";
        str += "To: " + m_tmail + "\r\n";
        str += "Subject: " + m_title + "\r\n";
        str += "Content-Type: multipart/mixed;boundary=qwertyuiop\r\n"; 
        str += "\r\n--qwertyuiop\r\n";
    
        if(m_type == "html"){
            str += "content-type:text/html;charset=utf-8\r\n"; //html类型
        }else{
            str += "Content-Type: text/plain;charset=utf-8\r\n"; //文本类型
        }
    
        Send(str);
        str = "\r\n" + m_content + "\r\n";
        str += "\r\n--qwertyuiop--\r\n.\r\n";
        Send(str);
        Recv();
        if(strstr(m_buf, "250") == nullptr) { return -1; }
        std::cout << "send success..." << std::endl;
    
    
        Send("QUIT\r\n");
    
        return 0;
    }
    
    int Smtp::make_connect() {
        m_sockfd = Socket(AF_INET, SOCK_STREAM, 0);
        
        hostent *host_info = gethostbyname(m_domain.c_str());
        if(host_info == nullptr){
            perror("gethostbyname error");
            return -1;
        }
    
        if(host_info->h_addrtype != AF_INET) {
            perror("AF_INET error");
            return -1;
        }
        char buf[128];
    
        struct sockaddr_in addr;
        addr.sin_family = AF_INET;
        addr.sin_addr.s_addr = *((unsigned long*)host_info->h_addr_list[0]);
        addr.sin_port = htons(m_port);
       
        Connect(m_sockfd, (struct sockaddr*)&addr, sizeof(addr));
    
        return 0;
    }
    
    void Smtp::Connect(int fd, const struct sockaddr *sa, socklen_t salen) {
        if(connect(fd, sa, salen)  == -1) {
            perror("connect error");
            exit(-1);
        }
    }
    
    int Smtp::Socket(int family, int type, int protocol) {
        int sockfd;
        if((sockfd = socket(family, type, protocol)) == -1) {
            perror("socket error");
            exit(-1);
        }
    
        return sockfd;
    }
    
    ssize_t Smtp::Send(const std::string& str) {
       ssize_t n;
       std::cout << str;
       if((n = send(m_sockfd, str.c_str(), str.length(), 0)) == -1) {
            perror("write error");
            exit(-1);
       }
    
        return n;
    }
    
    ssize_t Smtp::Recv() {
        ssize_t n;
        m_buf[0] = '\0';
        if((n == recv(m_sockfd, m_buf, 0xFFF, 0)) == -1) {
            perror("recv error");
            exit(-1);
        }
    
        return n;
    }
    
    std::string Smtp::enBase64(const std::string& str)   
    {
        std::string base64_table="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
        int str_len = str.length();
        std::string res="";
        for (int strp=0; strp<str_len/3*3; strp+=3)
        {
            res+=base64_table[str[strp]>>2];
            res+=base64_table[(str[strp]&0x3)<<4 | (str[strp+1])>>4];
            res+=base64_table[(str[strp+1]&0xf)<<2 | (str[strp+2])>>6];
            res+=base64_table[(str[strp+2])&0x3f];
        }
        if (str_len%3==1)
        {
            int pos=str_len/3 * 3;
            res += base64_table[str[pos]>>2];
            res += base64_table[(str[pos]&0x3)<<4];
            res += "=";
            res += "=";
        }
        else if (str_len%3==2)
        {
            int pos=str_len/3 * 3;
            res += base64_table[str[pos]>>2];
            res += base64_table[(str[pos]&0x3)<<4 | (str[pos+1])>>4];
            res += base64_table[(str[pos+1]&0xf)<<2];
            res += "=";
        }
        return res;
    }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
  • 相关阅读:
    MySQL-事务隔离-2
    基于python用telnet连接设备+查询指定文件内容的脚本
    独立站卖家如何让消费者沉淀为私域流量
    鸿鹄工程项目管理系统em Spring Cloud+Spring Boot+前后端分离构建工程项目管理系统
    8月22日计算机视觉理论学习笔记——常见 CNN
    如何实现一个IO口读取多个设备信息
    如何在电脑和手机设备上编辑只读 PDF
    Python 连接数据库添加字段
    【阿旭机器学习实战】【28】自己动手写一个单词拼写检查器---基于贝叶斯公式
    精通Git(四)——Git服务器
  • 原文地址:https://blog.csdn.net/weixin_45926547/article/details/125627700