zoukankan      html  css  js  c++  java
  • c++封装的发邮件类CSendMail

    项目需要做发邮件的功能,在网上找了一下代码,比较出名的SMailer编译不过(把那个Base64的encode拉到MailSender中实现就能过,但我搞不懂原来出错的原因,就不想用),另外找到了一个CSendMail的实现类,可以用,但代码的风格不好,使用起来也不方便,所以我就参考(chao)这两个类,实现了一个比较简单的发邮件类。CSendMail类支持多个收件人,支持附件(多个)。

    [原]:http://www.cnblogs.com/sixbeauty/p/3983525.html

    上代码,头文件:

    /*
    **CSendMail头文件
    **实现邮件的发送功能,支持多个用户接收,支持附件
    **program by six_beauty
    */
    
    #pragma once
    #include <string>
    #include <list>
    #include <map>
    #include "LogInfo.h"
    #include <winsock2.h>
    
    //类型定义
    const int MAX_BUFFER_SIZE = 255;                            //send和recv的缓存buffer的size
    const int SERVICE_PORT=25;                                    //端口
    
    typedef std::map<std::string, std::string> RECEIVERS;
    
    //CSendMail类
    class CSendMail
    {
    public:
        CSendMail();
        ~CSendMail();
    
    //////////////////////////////////////设置邮件信息/////////////////////////////////////////////////////////////////////////////////
        /////////////////////////connent///////////////////////////////////
        void setServerName(const std::string server_name);        //smtp服务器地址    
        void setUserName(const std::string user_name);            //邮箱用户名
        void setUserPwd(const std::string user_pwd);            //邮箱用户密码
        /////////////////////////SendMail//////////////////////////////////
        void setSenderName(const std::string sender_name);        //发送者的名字
        void setSenderAddress(const std::string sender_addr);    //发送者的邮箱(mail form:)
    
        //邮件接收者
        void setReceiver(const std::string name, const std::string address);            //先clear再add
        void addReceiver(const std::string name, const std::string address);            //增加邮件接收者,name是收件人名字,mail是地址
        void clearReceiver();                                                            //情况邮件接收者
    
        //添加附件
        void AddFilePath(std::string szFilePath);                                        //添加附件路径到附件列表中,一般的smtp服务器处理附件不超过50MB 
        void DeleteFilePath(std::string szFilePath);                                    //删除附件路径,如果有的话  
        void DeleteAllPath();                                                            //删除全部附件的路径  
    
    /////////////////////////////////////发送邮件//////////////////////////////////////////////////////////////////////////////////////
        //连接
        bool Connent();
        //邮件发送
        bool SendMail(const std::string mail_title,const std::string send_content);        //发送邮件的函数
    
    private:
        //功能函数
        inline std::string& replace_all(string& str,const string& old_value,const string& new_value);       //其实就是CString的Replace
        std::string GetFileName(std::string&szFilePath);        //从附件的路径中获取文件名称
        std::string GetFileData(std::string szFilePath);        //以字符形式读入附件内容
    
        std::string Base64Encode(std::string in_str);            //把char类型转换成Base64类型  
        //获取时间
        std::string prepareDate();
    
        //通信recv和send的封装
        int     sendRequest(const std::string content,bool bout=false);                //返回发送了多少字节
        bool rcvResponse(const std::string expected_response);    //返回接收的结果和expected_response是否相同
    
        //工作函数
        bool CReateSocket();                                    //建立socket连接  
        bool Logon();                                            //登录邮箱,主要进行发邮件前的准备工作  
    
        bool SendHead();                                        //发送邮件头  
        bool SendTextBody();                                    //发送邮件文本正文  
        bool SendFileBody();                                    //发送邮件附件  
        bool SendEnd();                                            //发送邮件结尾 
    
    
        SOCKET _socket;
        LogInfo m_logInfo;
    
        /////////////////////////邮件信息///////////////////////////////////
        /////////////////////////connent///////////////////////////////////
        std::string                    m_ServerName;        //smtp服务器地址
        std::string                    m_UserName;            //邮箱用户
        std::string                    m_UserPwd;            //邮箱用户密
        /////////////////////////SendMail//////////////////////////////////
        std::string                    m_SenderName;        //发送者的名    
        std::string                    m_SenderAddr;        //发送者的邮箱(mail form:)
        std::string                    m_MailTitle;        //邮件标题(subject)
        std::string                    m_TextBody;            //邮件正文
    
        RECEIVERS                    m_Receivers;        //邮件接收者(name,email_address)
    
        std::list<std::string>        m_FilePathList;        //附件路径_list
    
        /////////////////////////邮件信息///////////////////////////////////
    };

    源文件:

    /*
    **CSendMail源文件
    **实现邮件的发送功能,支持多个用户接收,支持附件
    **program by six_beauty
    */
    
    //#include <afx.h>
    #include "CSendMail.h"
    #include "time.h"
    #include <sstream>
    #include <fstream>
    #pragma comment(lib,"WSOCK32")  
    #pragma comment(lib, "ws2_32")
    
    
    const std::string _AppOctStrmContent_encode_chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    
    //类的实现
    
    CSendMail::CSendMail(void)  
    {  
    }  
    
    
    CSendMail::~CSendMail(void)  
    {  
        clearReceiver();
        DeleteAllPath();
    }  
    
    //连接
    bool CSendMail::Connent()
    {
        //邮件信息设置判断
        if(m_ServerName.empty() || m_UserName.empty() || m_UserPwd.empty())
        {
            m_logInfo.logInfo("Connect 失败,请先设置邮件登陆信息!");
            return false;
        }
    
        if(!CReateSocket())//建立连接  
        {  
            m_logInfo.logInfo("建立连接失败!");
            return false;  
        }
    
        if(!Logon())//建立连接  
        {  
            m_logInfo.logInfo("登陆失败!");
            return false;  
        }
        return true;
    }
    
    
    //发送邮件的函数送
    bool CSendMail::SendMail(const std::string mail_title,const std::string send_content)                                        
    {
        //参数赋值
        m_MailTitle=mail_title;
        m_TextBody=send_content;
    
        if(m_SenderName.empty() || m_SenderAddr.empty() || m_Receivers.empty())
        {
            m_logInfo.logInfo("[SendMail]邮件参数设置错误,请检查邮件发送设置信息是否完整!");
            return false;
        }
    
        if(!SendHead())//发送邮件头  
        {  
            m_logInfo.logInfo("发送邮件头失败!");
            return false;  
        }  
    
        if(!SendTextBody())//发送邮件文本部分  
        {  
            return false;  
        }  
    
        if(!SendFileBody())//发送附件  
        {  
            return false;  
        }  
    
        if(!SendEnd())//结束邮件,并关闭sock  
        {  
            return false;  
        }  
    
        return true;
    }
    
    
    ////////////////////////////////////////////设置邮件信息/////////////////////////////////////////////////////////////////////
    void CSendMail::setServerName(const std::string server_name)        //smtp服务器地址    
    {
        m_ServerName=server_name;
    }
    
    void CSendMail::setUserName(const std::string user_name)            //邮箱用户名
    {
        m_UserName=user_name;
    }
    
    void CSendMail::setUserPwd(const std::string user_pwd)                //邮箱用户密码
    {
        m_UserPwd=user_pwd;
    }
    
    void CSendMail::setSenderName(const std::string sender_name)        //发送者的名字
    {
        m_SenderName=sender_name;
    }
    
    void CSendMail::setSenderAddress(const std::string sender_addr)    //发送者的邮箱(mail form:)
    {
        m_SenderAddr=sender_addr;
    }
    
    
    void CSendMail::addReceiver(const std::string name, const std::string address)
    {
        m_Receivers.insert(RECEIVERS::value_type(name, address));
    }
    
    void CSendMail::setReceiver(const std::string name, const std::string address)
    {
        m_Receivers.clear();
        m_Receivers.insert(RECEIVERS::value_type(name, address));
    }
    
    void CSendMail::clearReceiver()
    {
        m_Receivers.clear();
    }
    
    void CSendMail::AddFilePath(std::string szFilePath)//添加附件路径  
    {  
        for(std::list<std::string>::iterator itrList=m_FilePathList.begin();itrList!=m_FilePathList.end();++itrList)
        {
            if( itrList->compare(szFilePath) == 0 )
            {
                //已经存在
                return ;
            }
        }
        //还未加入
        m_FilePathList.push_back(szFilePath);
    }  
    
    void CSendMail::DeleteFilePath(std::string szFilePath)//删除附件路径  
    {  
        for(std::list<std::string>::iterator itrList=m_FilePathList.begin();itrList!=m_FilePathList.end();)
        {
            if( itrList->compare(szFilePath) == 0 )
            {
                itrList = m_FilePathList.erase(itrList);
            }else
            {
                itrList++;
            }
        }
    }  
    
    void CSendMail::DeleteAllPath(void)  
    {  
        m_FilePathList.clear();  
    }  
    
    
    ////////////////////////////////////////////功能函数///////////////////////////////////////////////////////////////////
    
    //实现CString的Replace
    string& CSendMail::replace_all(string& str,const string& old_value,const string& new_value)
    {   
        while(true)
        {   
            string::size_type pos(0);   
            if( (pos=str.find(old_value))!=string::npos )   
                str.replace(pos,old_value.length(),new_value);   
            else
                break;   
        }   
        return str;   
    }
    
    //从附件的路径中获取文件名称
    std::string CSendMail::GetFileName(std::string &szFilePath)
    {
        replace_all(szFilePath,"/","\");
        string szFileName=szFilePath.substr(szFilePath.rfind("\")+1,szFilePath.length());
        return szFileName;
    }
    
    //以字符形式读入附件内容
    std::string CSendMail::GetFileData(std::string szFilePath)
    {
        std::string szBuffer;
        if(szFilePath.empty())  
        {
            m_logInfo.logInfo("[SendFileBody]Error:附件路径为空!");
            return szBuffer;  
        }  
    
        ifstream ifFile(szFilePath.c_str(),ios::binary|ios::in);
        if(!ifFile)  
        {  
            m_logInfo.logInfo("[SendFileBody]Error:打开附件路径错误!");
            return szBuffer;  
        }  
        ifFile.seekg(0,ios::beg);
        std::ostringstream tmp; 
        tmp << ifFile.rdbuf(); 
        szBuffer = tmp.str();    
        ifFile.close();
    
        return szBuffer;  
    }
    
    //把char类型转换成Base64类型 
    std::string CSendMail::Base64Encode(std::string in_str)        
    {
        std::string out_str;
        unsigned char c1, c2, c3;
        int i = 0;
        int len = in_str.length();
    
        while ( i<len )
        {
            // read the first byte
            c1 = in_str[i++];
            if ( i==len )       // pad with "="
            {
                out_str += _AppOctStrmContent_encode_chars[ c1>>2 ];
                out_str += _AppOctStrmContent_encode_chars[ (c1&0x3)<<4 ];
                out_str += "==";
                break;
            }
    
            // read the second byte
            c2 = in_str[i++];
            if ( i==len )       // pad with "="
            {
                out_str += _AppOctStrmContent_encode_chars[ c1>>2 ];
                out_str += _AppOctStrmContent_encode_chars[ ((c1&0x3)<<4) | ((c2&0xF0)>>4) ];
                out_str += _AppOctStrmContent_encode_chars[ (c2&0xF)<<2 ];
                out_str += "=";
                break;
            }
    
            // read the third byte
            c3 = in_str[i++];
            // convert into four bytes string
            out_str += _AppOctStrmContent_encode_chars[ c1>>2 ];
            out_str += _AppOctStrmContent_encode_chars[ ((c1&0x3)<<4) | ((c2&0xF0)>>4) ];
            out_str += _AppOctStrmContent_encode_chars[ ((c2&0xF)<<2) | ((c3&0xC0)>>6) ];
            out_str += _AppOctStrmContent_encode_chars[ c3&0x3F ];
        }
    
        return out_str;
    }
    
    int CSendMail::sendRequest(const std::string content,bool bout)
    {
        int len_s=send(_socket, content.c_str(), content.length(), 0);
        if ( len_s < 0 )
        {
            m_logInfo.logInfo("[ERROR]SEND:%s",content.c_str());
            return false;
        }
        //输出信息
        if(bout)
        {
            m_logInfo.logInfo("[INFO]SEND:%s",content.c_str());
        }
        return len_s;
    }
    
    bool CSendMail::rcvResponse(const std::string expected_response)
    {
        int recv_bytes = 0;
        char response_buffer[MAX_BUFFER_SIZE];
        if ( (recv_bytes = recv(_socket, response_buffer, MAX_BUFFER_SIZE, 0)) < 0 )
        {
            m_logInfo.logInfo("[ERROR]RECV:%s",expected_response.c_str());
            return false;
        }
        //输出信息
        std::string response(response_buffer, recv_bytes);
        m_logInfo.logInfo("[INFO]RECV(%s):%s",expected_response.c_str(),response.c_str());
        if ( response.substr(0, 3) != expected_response )
        {
            return false;
        }
        return true;
    }
    
    std::string CSendMail::prepareDate()
    {
        char date_string[MAX_BUFFER_SIZE];
    
        time_t seconds;
        time(&seconds);
        strftime(date_string, MAX_BUFFER_SIZE, 
            "%a, %d %b %y %H:%M:%S +0800", 
            localtime(&seconds));          // +0800 maybe hard code
    
        return date_string;
    }
    
    ////////////////////////////////////////////////工作函数//////////////////////////////////////////////////////////////////////
    
    bool CSendMail::CReateSocket()  
    {  
        WORD wVersionRequested;  
        WSADATA wsaData;  
        int err;  
        wVersionRequested = MAKEWORD( 2, 2 );  
        err = WSAStartup( wVersionRequested, &wsaData );  
        if ( err != 0 )   
        {  
            m_logInfo.logInfo("WSAStartup调用失败!");
            return false;  
        }  
        if ( LOBYTE( wsaData.wVersion ) != 2 ||  
            HIBYTE( wsaData.wVersion ) != 2 )  
        {  
            WSACleanup( );  
            return false;   
        }  
        _socket = socket(AF_INET,SOCK_STREAM,IPPROTO_IP);  
        if (_socket == INVALID_SOCKET)  
        {  
            m_logInfo.logInfo("socket创建失败!");
            return false;  
        }  
    
        sockaddr_in servaddr;  
        memset(&servaddr,0,sizeof(sockaddr_in));  
        servaddr.sin_family = AF_INET;  
        servaddr.sin_port = htons(SERVICE_PORT);//发邮件一般都是25端口  
    
        struct hostent *hp=gethostbyname(m_ServerName.c_str());//使用名称  
        if (hp == NULL)
        {
            DWORD dwErrCode =    GetLastError();
            return false;
        }
        servaddr.sin_addr.s_addr=*(int*)(*hp->h_addr_list);  
    
    
        int ret = connect(_socket,(sockaddr*)&servaddr,sizeof(servaddr));//建立连接  
        if (ret == SOCKET_ERROR)  
        {  
            DWORD dwErr = GetLastError();
            return false;  
        }  
        if(!rcvResponse("220"))
            return false;
        return true;  
    }  
    
    bool CSendMail::Logon()
    {
        char local_host[MAX_BUFFER_SIZE];
        if ( gethostname(local_host, MAX_BUFFER_SIZE) != 0 )
        {
            m_logInfo.logInfo("Get local host name error!");
            return false;
        }
        
        std::string msg;
        
        msg  = "HELO ";
        msg += std::string(local_host) + "
    ";
        sendRequest(msg);
        if(!rcvResponse("250"))
        {
            return false;
        }
    
        msg = "AUTH LOGIN
    ";
        sendRequest(msg);
        if(!rcvResponse("334"))
        {
            return false;
        }
          
    
        msg = Base64Encode(m_UserName) + "
    ";
        sendRequest(msg);
        if(!rcvResponse("334"))
        {
            return false;
        }
    
        msg = Base64Encode(m_UserPwd) + "
    ";
        sendRequest(msg);
        if(!rcvResponse("235"))
        {
            return false;
        }
    
        return true;//登录成功  
    }
    
    ///////////////////////////////////SendMail////////////////////////////////////////////////////
    //发送邮件头 
    bool CSendMail::SendHead()
    {
        std::string msg;
    
        msg  = "MAIL FROM:<";
        msg += m_SenderAddr + ">
    ";
        sendRequest(msg);
        if(!rcvResponse("250"))
        {
            m_logInfo.logInfo("邮件地址错误:%s",m_SenderAddr.c_str());
            return false;
        }
    
        //遍历获得receiver
        for(RECEIVERS::iterator itrRec=m_Receivers.begin();itrRec!=m_Receivers.end();itrRec++)
        {
            msg  = "RCPT TO: <";
            msg += itrRec->second + ">
    ";
            sendRequest(msg);
            if(!rcvResponse("250"))
            {
                return false;
            }    
        }
    
        msg  = "DATA
    ";
        sendRequest(msg);
        if(!rcvResponse("354"))
        {
            return false;
        }
    
        //发送Headers
        msg  = "From:"" + m_SenderName + ""<" + m_SenderAddr + ">
    ";
    
        //遍历receiver
        msg += "To: ";    
        for(RECEIVERS::iterator itrRec=m_Receivers.begin();itrRec!=m_Receivers.end();itrRec++)
        {
            std::string szRecv;
            szRecv = """ + itrRec->first + ""<" + itrRec->second + ">, ";
            msg += szRecv;
        }
        msg += "
    ";
    
        msg += "Date: ";
        msg += prepareDate() + "
    ";
    
        msg += "Subject: ";
        msg += m_MailTitle + "
    ";
    
        msg += "X-Mailer: six_beauty 
    ";
    
        msg += "MIME-Version: 1.0
    ";
        msg += "Content-type: multipart/mixed;  boundary="INVT"
    
    ";
    
        msg += "
    ";
        sendRequest(msg);
    
        return true;  
    }
    
    bool CSendMail::SendTextBody()  
    {  
        std::string msg;
        msg  = "--INVT
    Content-Type: text/plain;
      charset="gb2312"
    
    ";
        msg += m_TextBody;
        msg += "
    
    ";
        int len_s=sendRequest(msg,true);
    
        if(len_s != msg.length())
        {
            m_logInfo.logInfo("发送邮件正文出错,应该发送长度(%d):实际发送长度(%d)",msg.length(),len_s);
            return false;
        }
    
        return true;    
    }  
    
    
    bool CSendMail::SendFileBody()  
    {  
        std::string msg;
        //遍历发送附件文件
        for(std::list<std::string>::iterator itrList=m_FilePathList.begin();itrList!=m_FilePathList.end();itrList++)
        {
            std::string filePath=*itrList;
            std::string fileName=GetFileName(filePath);
            std::string szContent=GetFileData(filePath);
            
            msg  = "--INVT
    Content-Type: application/octet-stream;
      name="";
            msg += fileName;
            msg += ""
    Content-Transfer-Encoding: base64
    Content-Disposition: attachment;
      filename="";
            msg += fileName;
            msg += ""
    
    ";
            sendRequest(msg,true);
    
            int npos=0,len=szContent.length();
            while(npos<len)
            {
                std::string szBuffer=Base64Encode(szContent.substr(npos,min(len-npos,3000)));
                szBuffer+="
    ";
                sendRequest(szBuffer);
                npos += min(len-npos,3000);
            }
        }
    
        return true;  
    }  
    
    bool CSendMail::SendEnd()  
    {  
        std::string msg;
    
        msg = "--INVT--
    .
    ";
        sendRequest(msg,true);
    
        msg = "QUIT
    ";
        sendRequest(msg,true);
    
        closesocket(_socket);  
        WSACleanup();
        
        return true;  
    }  

    然后是需要用户实现的一个输出类,请自己实现输出的方式,这里直接cout出来。

    #include<iostream>
    #include<stdarg.h>
    
    using namespace std;
    
    const int BUF_SIZE=4096;
    //实现输出类
    class LogInfo
    {
    public:
        LogInfo(){};
        ~LogInfo(){};
    
    
        void logInfo(char *szFormat,...)
        {
            char szBuf[BUF_SIZE]={};
            va_list args;                            //第一步
            va_start(args,szFormat);                 //第二步
            _vsnprintf(szBuf,BUF_SIZE,szFormat,args);    //第三步
            va_end(args);                            //第四步
    
            //在这是实现输出方式
            std::cout<<szBuf<<endl;
            return ;
        }
    };

    最后是测试代码:

    #include "CSendMail.h"
    #include "iostream"
    using namespace std;
    
    
    int main()
    {
        CSendMail sMailer;
        //    freopen("F://mailfile//out.txt","w+",stdout);
    
        sMailer.setServerName("smtp.126.com");                                                        //邮箱smtp,如"smtp.126.com"
        sMailer.setUserName("userName");                                                                //邮箱账号名,如"****@126.com"
        sMailer.setUserPwd("userPwd");                                                                //邮箱密码
        sMailer.setSenderName("陈家喵");                                                                //发件人名字
        sMailer.setSenderAddress("sanyue9394@126.com");                                                //发送邮箱地址,填你账号的地址,上面的邮箱账号名"****@126.com"
    
        sMailer.setReceiver("柯林林","kelinting@163.com");                                            //添加邮件接收者
        sMailer.addReceiver("sixbeauty","sanyue9394@126.com");
    
        sMailer.AddFilePath("F:\mailfile\out.txt");            //添加附件
    //    sMailer.AddFilePath("F:/mailfile/libcurl.exp");                                                //添加附件
    
        //发送第一份邮件
        if(sMailer.Connent())                                                                        //每次发邮件前都需要connect
        {
            if(sMailer.SendMail("CMailSender:阿垃垃圾君","你想快速发家致富吗?你想一夜成名吗?访问 http://www.sb.com"))            //第一个字符串是邮件标题,第二个是邮件内容
                cout<<"邮件发送完成!";
    
        }    
    
        //发送第二份邮件
        if(sMailer.Connent())                                                                        //每次发邮件前都需要connect
        {
            std::string title,content;
            title="测试邮件part_2";
            content="柯林是个大八嘎!";
            if(sMailer.SendMail(title,content))                                                        //第一个字符串是邮件标题,第二个是邮件内容
                cout<<"邮件发送完成!";
    
        }
    
    
        return 0;
    }

    测试时,把

    sMailer.setUserName("userName");                                                          
    sMailer.setUserPwd("userPwd");

    改了就可以发送邮件了。

    附实现好的工程下载:http://download.csdn.net/detail/biantaiwangzi/7948843

    参考:

    比较出名也写得比较好的SMailer【一组实现邮件发送功能的C++封装类】:http://morningspace.51.net/resource/SMailer.php

    实现逻辑抄自【C++发送邮件和附件】:http://blog.csdn.net/ybjx111/article/details/7031055#

  • 相关阅读:
    Android 开发 深入理解Handler、Looper、Messagequeue 转载
    Android 开发 Handler的基本使用
    Java 学习 注解
    Android 开发 AlarmManager 定时器
    Android 开发 框架系列 百度语音合成
    Android 开发 框架系列 Google的ORM框架 Room
    Android 开发 VectorDrawable 矢量图 (三)矢量图动画
    Android 开发 VectorDrawable 矢量图 (二)了解矢量图属性与绘制
    Android 开发 VectorDrawable 矢量图 (一)了解Android矢量图与获取矢量图
    Android 开发 知晓各种id信息 获取线程ID、activityID、内核ID
  • 原文地址:https://www.cnblogs.com/sixbeauty/p/3983525.html
Copyright © 2011-2022 走看看