邮件正文及其附件的发送的C++实现

来源:互联网 发布:外文期刊数据库检索 编辑:程序博客网 时间:2024/06/05 00:50

来源


这段代码我花了整整一天来编写,如果转载,请注明出处,谢谢!

   前面的一篇文章已经讲了如何发送邮件正文,原理我就不再叙述了,要了解的同学请到这里查看!

   http://blog.csdn.net/lishuhuakai/article/details/27503503

   网上很多发送邮件附件的代码都不能用,所以我用心写了一个,直接封装成了一个类,需要的同学可以直接调用这个类来发送邮件,使用的是纯C++,不含MFC,请放心使用。(在VS2013下测试完美通过!)

   废话不多说,直接上代码!

   Smtp.h

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. #include <WinSock2.h>  
  2. #include <iostream>  
  3. #include <string>  
  4. #include <list>  
  5. using namespace std;  
  6.   
  7. #pragma  comment(lib, "ws2_32.lib") /*链接ws2_32.lib动态链接库*/  
  8. const int MAXLEN = 1024;  
  9. const int MAX_FILE_LEN = 6000;  
  10.   
  11. static const char base64Char[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";  
  12.   
  13. struct FILEINFO /*用来记录文件的一些信息*/  
  14. {  
  15.     char fileName[128]; /*文件名称*/  
  16.     char filePath[256]; /*文件绝对路径*/  
  17. };  
  18.   
  19. class CSmtp  
  20. {  
  21. public:  
  22.     CSmtp(void);  
  23.     CSmtp(  
  24.         int port,  
  25.         string srvDomain,   //smtp服务器域名  
  26.         string userName,    //用户名  
  27.         string password,    //密码  
  28.         string targetEmail, //目的邮件地址  
  29.         string emailTitle,  //主题  
  30.         string content       //内容  
  31.         );  
  32. public:  
  33.     ~CSmtp(void);  
  34. public:  
  35.     int port;  
  36. public:  
  37.     string domain;  
  38.     string user;  
  39.     string pass;  
  40.     string targetAddr;  
  41.     string title;  
  42.     string content;  
  43.     /*为了方便添加文件,删除文件神马的,使用list容器最为方便,相信大家在数据结构里面都学过*/  
  44.     list <FILEINFO *> listFile;  
  45.       
  46. public:  
  47.     char buff[MAXLEN + 1];  
  48.     int buffLen;  
  49.     SOCKET sockClient;  //客户端的套接字  
  50. public:  
  51.     BOOL CreateConn(); /*创建连接*/  
  52.   
  53.     BOOL Send(string &message);  
  54.     BOOL Recv();  
  55.   
  56.     void FormatEmailHead(string &email);//格式化要发送的邮件头部  
  57.     int Login();  
  58.     BOOL SendEmailHead();       //发送邮件头部信息  
  59.     BOOL SendTextBody();        //发送文本信息  
  60.     //BOOL SendAttachment();        //发送附件  
  61.     int SendAttachment_Ex();  
  62.     BOOL SendEnd();  
  63. public:  
  64.     void AddAttachment(string &filePath); //添加附件  
  65.     void DeleteAttachment(string &filePath); //删除附件  
  66.     void DeleteAllAttachment(); //删除所有的附件  
  67.     int SendEmail_Ex();  
  68.     /*关于错误码的说明:1.网络错误导致的错误2.用户名错误3.密码错误4.文件不存在0.成功*/  
  69.     char* base64Encode(char const* origSigned, unsigned origLength);  
  70. };  

     Smtp.cpp

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. #include "stdafx.h"  
  2. #include "Smtp.h"  
  3. #include <iostream>  
  4. #include <fstream>  
  5. using namespace std;  
  6.   
  7. /*base64采用别人的编码,不过,这不是重点,重点是我完成了我的一个比较好的邮件发送客户端*/  
  8. char* CSmtp::base64Encode(char const* origSigned, unsigned origLength)  
  9. {  
  10.     unsigned char const* orig = (unsigned char const*)origSigned; // in case any input bytes have the MSB set  
  11.     if (orig == NULL) return NULL;  
  12.   
  13.     unsigned const numOrig24BitValues = origLength / 3;  
  14.     bool havePadding = origLength > numOrig24BitValues * 3;  
  15.     bool havePadding2 = origLength == numOrig24BitValues * 3 + 2;  
  16.     unsigned const numResultBytes = 4 * (numOrig24BitValues + havePadding);  
  17.     char* result = new char[numResultBytes + 3]; // allow for trailing '/0'  
  18.   
  19.     // Map each full group of 3 input bytes into 4 output base-64 characters:  
  20.     unsigned i;  
  21.     for (i = 0; i < numOrig24BitValues; ++i)  
  22.     {  
  23.         result[4 * i + 0] = base64Char[(orig[3 * i] >> 2) & 0x3F];  
  24.         result[4 * i + 1] = base64Char[(((orig[3 * i] & 0x3) << 4) | (orig[3 * i + 1] >> 4)) & 0x3F];  
  25.         result[4 * i + 2] = base64Char[((orig[3 * i + 1] << 2) | (orig[3 * i + 2] >> 6)) & 0x3F];  
  26.         result[4 * i + 3] = base64Char[orig[3 * i + 2] & 0x3F];  
  27.     }  
  28.   
  29.     // Now, take padding into account.  (Note: i == numOrig24BitValues)  
  30.     if (havePadding)  
  31.     {  
  32.         result[4 * i + 0] = base64Char[(orig[3 * i] >> 2) & 0x3F];  
  33.         if (havePadding2)  
  34.         {  
  35.             result[4 * i + 1] = base64Char[(((orig[3 * i] & 0x3) << 4) | (orig[3 * i + 1] >> 4)) & 0x3F];  
  36.             result[4 * i + 2] = base64Char[(orig[3 * i + 1] << 2) & 0x3F];  
  37.         }  
  38.         else  
  39.         {  
  40.             result[4 * i + 1] = base64Char[((orig[3 * i] & 0x3) << 4) & 0x3F];  
  41.             result[4 * i + 2] = '=';  
  42.         }  
  43.         result[4 * i + 3] = '=';  
  44.     }  
  45.   
  46.     result[numResultBytes] = '\0';  
  47.     return result;  
  48. }  
  49. CSmtp::CSmtp(void)  
  50. {  
  51.     this->content = "";  
  52.     this->port = 25;  
  53.     this->user = "";  
  54.     this->pass = "";  
  55.     this->targetAddr = "";  
  56.     this->title = "";  
  57.     this->domain = "";  
  58.   
  59.     WORD wVersionRequested;  
  60.     WSADATA wsaData;  
  61.     int err;  
  62.     wVersionRequested = MAKEWORD(2, 1);  
  63.     err = WSAStartup(wVersionRequested, &wsaData);  
  64.     this->sockClient = 0;  
  65.   
  66. }  
  67.   
  68. CSmtp::~CSmtp(void)  
  69. {  
  70.     DeleteAllAttachment();   
  71.     closesocket(sockClient);  
  72.     WSACleanup();  
  73. }  
  74.   
  75.   
  76. CSmtp::CSmtp(  
  77.              int port,  
  78.              string srvDomain,   
  79.              string userName,   
  80.              string password,   
  81.              string targetEmail,  
  82.              string emailTitle,   
  83.              string content  
  84.              )  
  85. {  
  86.     this->content = content;  
  87.     this->port = port;  
  88.     this->user = userName;  
  89.     this->pass = password;  
  90.     this->targetAddr = targetEmail;  
  91.     this->title = emailTitle;  
  92.     this->domain = srvDomain;  
  93.   
  94.     WORD wVersionRequested;  
  95.     WSADATA wsaData;  
  96.     int err;  
  97.     wVersionRequested = MAKEWORD(2, 1);  
  98.     err = WSAStartup(wVersionRequested, &wsaData);  
  99.     this->sockClient = 0;  
  100. }  
  101.   
  102. BOOL CSmtp::CreateConn()  
  103. {  
  104.     //为建立socket对象做准备,初始化环境  
  105.     SOCKET sockClient = socket(AF_INET,SOCK_STREAM,0); //建立socket对象  
  106.     SOCKADDR_IN addrSrv;   
  107.     HOSTENT* pHostent;  
  108.     pHostent = gethostbyname(domain.c_str());  //得到有关于域名的信息  
  109.   
  110.     addrSrv.sin_addr.S_un.S_addr = *((DWORD *)pHostent->h_addr_list[0]); //得到smtp服务器的网络字节序的ip地址     
  111.     addrSrv.sin_family = AF_INET;     
  112.     addrSrv.sin_port = htons(port);     
  113.     int err = connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));   //向服务器发送请求   
  114.     if(err != 0)  
  115.     {  
  116.         return FALSE;  
  117.         //printf("链接失败\n");  
  118.     }  
  119.     this->sockClient = sockClient;  
  120.     if (FALSE == Recv())  
  121.     {  
  122.         return FALSE;  
  123.     }  
  124.     return TRUE;  
  125. }  
  126.   
  127. BOOL CSmtp::Send(string &message)  
  128. {  
  129.     int err = send(sockClient, message.c_str(), message.length(), 0);  
  130.     if (err == SOCKET_ERROR)  
  131.     {  
  132.         return FALSE;  
  133.     }  
  134.     cout << message << endl;  
  135.     return TRUE;  
  136. }  
  137.   
  138. BOOL CSmtp::Recv()   
  139. {  
  140.     memset(buff, 0, sizeof(char)* (MAXLEN + 1));  
  141.     int err = recv(sockClient, buff, MAXLEN, 0); //接收数据  
  142.     if (err == SOCKET_ERROR)  
  143.     {  
  144.         return FALSE;  
  145.     }  
  146.     buff[err] = '\0';  
  147.     cout << buff << endl;  
  148.     return TRUE;  
  149. }  
  150.   
  151. int CSmtp::Login()  
  152. {  
  153.     string sendBuff;  
  154.     sendBuff = "EHLO ";  
  155.     sendBuff += user;  
  156.     sendBuff += "\r\n";  
  157.   
  158.     if (FALSE == Send(sendBuff) || FALSE == Recv()) //既接收也发送  
  159.     {  
  160.         return 1; /*1表示发送失败由于网络错误*/  
  161.     }  
  162.   
  163.     sendBuff.empty();  
  164.     sendBuff = "AUTH LOGIN\r\n";  
  165.     if (FALSE == Send(sendBuff) || FALSE == Recv()) //请求登陆  
  166.     {  
  167.         return 1; /*1表示发送失败由于网络错误*/  
  168.     }  
  169.   
  170.     sendBuff.empty();  
  171.     int pos = user.find('@', 0);  
  172.     sendBuff = user.substr(0, pos); //得到用户名  
  173.   
  174.     char *ecode;  
  175.     /*在这里顺带扯一句,关于string类的length函数与C语言中的strlen函数的区别,strlen计算出来的长度,只到'\0'字符为止,而string::length()函数实际上返回的是string类中字符数组的大小,你自己可以测试一下,这也是为什么我下面不使用string::length()的原因*/  
  176.   
  177.     ecode = base64Encode(sendBuff.c_str(), strlen(sendBuff.c_str()));  
  178.     sendBuff.empty();  
  179.     sendBuff = ecode;  
  180.     sendBuff += "\r\n";  
  181.     delete []ecode;  
  182.   
  183.     if (FALSE == Send(sendBuff) || FALSE == Recv()) //发送用户名,并接收服务器的返回  
  184.     {  
  185.         return 1; /*错误码1表示发送失败由于网络错误*/  
  186.     }  
  187.   
  188.     sendBuff.empty();  
  189.     ecode = base64Encode(pass.c_str(), strlen(pass.c_str()));  
  190.     sendBuff = ecode;  
  191.     sendBuff += "\r\n";  
  192.     delete []ecode;  
  193.   
  194.     if (FALSE == Send(sendBuff) || FALSE == Recv()) //发送用户密码,并接收服务器的返回  
  195.     {  
  196.         return 1; /*错误码1表示发送失败由于网络错误*/  
  197.     }  
  198.   
  199.     if (NULL != strstr(buff, "550"))  
  200.     {  
  201.         return 2;/*错误码2表示用户名错误*/  
  202.     }  
  203.   
  204.     if (NULL != strstr(buff, "535")) /*535是认证失败的返回*/  
  205.     {  
  206.         return 3; /*错误码3表示密码错误*/  
  207.     }  
  208.     return 0;  
  209. }  
  210.   
  211. BOOL CSmtp::SendEmailHead()     //发送邮件头部信息  
  212. {  
  213.     string sendBuff;  
  214.     sendBuff = "MAIL FROM: <" + user + ">\r\n";  
  215.     if (FALSE == Send(sendBuff) || FALSE == Recv())  
  216.     {  
  217.         return FALSE; /*表示发送失败由于网络错误*/  
  218.     }  
  219.   
  220.   
  221.     sendBuff.empty();  
  222.     sendBuff = "RCPT TO: <" + targetAddr + ">\r\n";  
  223.     if (FALSE == Send(sendBuff) || FALSE == Recv())  
  224.     {  
  225.         return FALSE; /*表示发送失败由于网络错误*/  
  226.     }  
  227.   
  228.     sendBuff.empty();  
  229.     sendBuff = "DATA\r\n";  
  230.     if (FALSE == Send(sendBuff) || FALSE == Recv())  
  231.     {  
  232.         return FALSE; //表示发送失败由于网络错误  
  233.     }  
  234.   
  235.     sendBuff.empty();  
  236.     FormatEmailHead(sendBuff);  
  237.     if (FALSE == Send(sendBuff))   
  238.         //发送完头部之后不必调用接收函数,因为你没有\r\n.\r\n结尾,服务器认为你没有发完数据,所以不会返回什么值  
  239.     {  
  240.         return FALSE; /*表示发送失败由于网络错误*/  
  241.     }  
  242.     return TRUE;  
  243. }  
  244.   
  245. void CSmtp::FormatEmailHead(string &email)  
  246. {/*格式化要发送的内容*/  
  247.     email = "From: ";  
  248.     email += user;  
  249.     email += "\r\n";  
  250.   
  251.     email += "To: ";  
  252.     email += targetAddr;  
  253.     email += "\r\n";  
  254.   
  255.     email += "Subject: ";  
  256.     email += title;  
  257.     email += "\r\n";  
  258.   
  259.     email += "MIME-Version: 1.0";  
  260.     email += "\r\n";  
  261.   
  262.     email += "Content-Type: multipart/mixed;boundary=qwertyuiop";  
  263.     email += "\r\n";  
  264.     email += "\r\n";  
  265. }  
  266.   
  267. BOOL CSmtp::SendTextBody()  /*发送邮件文本*/  
  268. {  
  269.     string sendBuff;  
  270.     sendBuff = "--qwertyuiop\r\n";  
  271.     sendBuff += "Content-Type: text/plain;";  
  272.     sendBuff += "charset=\"gb2312\"\r\n\r\n";  
  273.     sendBuff += content;  
  274.     sendBuff += "\r\n\r\n";  
  275.     return Send(sendBuff);  
  276. }  
  277.   
  278. int CSmtp::SendAttachment_Ex() /*发送附件*/  
  279. {  
  280.     for (list<FILEINFO *>::iterator pIter = listFile.begin(); pIter != listFile.end(); pIter++)  
  281.     {  
  282.         cout << "Attachment is sending ~~~~~" << endl;  
  283.         cout << "Please be patient!" << endl;  
  284.         string sendBuff;  
  285.         sendBuff = "--qwertyuiop\r\n";  
  286.         sendBuff += "Content-Type: application/octet-stream;\r\n";  
  287.         sendBuff += " name=\"";  
  288.         sendBuff += (*pIter)->fileName;  
  289.         sendBuff += "\"";  
  290.         sendBuff += "\r\n";  
  291.   
  292.         sendBuff += "Content-Transfer-Encoding: base64\r\n";  
  293.         sendBuff += "Content-Disposition: attachment;\r\n";  
  294.         sendBuff += " filename=\"";  
  295.         sendBuff += (*pIter)->fileName;  
  296.         sendBuff += "\"";  
  297.   
  298.         sendBuff += "\r\n";  
  299.         sendBuff += "\r\n";  
  300.         Send(sendBuff);  
  301.         ifstream ifs((*pIter)->filePath,ios::in | ios::binary);  
  302.         if (FALSE == ifs.is_open())  
  303.         {  
  304.             return 4; /*错误码4表示文件打开错误*/  
  305.         }  
  306.         char fileBuff[MAX_FILE_LEN];  
  307.         char *chSendBuff;  
  308.         memset(fileBuff, 0, sizeof(fileBuff));  
  309.         /*文件使用base64加密传送*/  
  310.         while (ifs.read(fileBuff, MAX_FILE_LEN))  
  311.         {  
  312.             //cout << ifs.gcount() << endl;  
  313.             chSendBuff = base64Encode(fileBuff, MAX_FILE_LEN);  
  314.             chSendBuff[strlen(chSendBuff)] = '\r';  
  315.             chSendBuff[strlen(chSendBuff)] = '\n';  
  316.             send(sockClient, chSendBuff, strlen(chSendBuff), 0);  
  317.             delete[]chSendBuff;  
  318.         }  
  319.         //cout << ifs.gcount() << endl;  
  320.         chSendBuff = base64Encode(fileBuff, ifs.gcount());  
  321.         chSendBuff[strlen(chSendBuff)] = '\r';  
  322.         chSendBuff[strlen(chSendBuff)] = '\n';  
  323.         int err = send(sockClient, chSendBuff, strlen(chSendBuff), 0);  
  324.   
  325.         if (err != strlen(chSendBuff))  
  326.         {  
  327.             cout << "文件传送出错!" << endl;  
  328.             return 1;  
  329.         }  
  330.         delete []chSendBuff;  
  331.     }  
  332.     return 0;  
  333. }  
  334.   
  335. BOOL CSmtp::SendEnd() /*发送结尾信息*/  
  336. {  
  337.     string sendBuff;  
  338.     sendBuff = "--qwertyuiop--";  
  339.     sendBuff += "\r\n.\r\n";  
  340.     if (FALSE == Send(sendBuff) || FALSE == Recv())  
  341.     {  
  342.         return FALSE;  
  343.     }  
  344.     cout << buff << endl;  
  345.     sendBuff.empty();  
  346.     sendBuff = "QUIT\r\n";  
  347.     return (Send(sendBuff) && Recv());  
  348. }  
  349.   
  350. int CSmtp::SendEmail_Ex()  
  351. {  
  352.     if (FALSE == CreateConn())  
  353.     {  
  354.         return 1;  
  355.     }  
  356.     //Recv();  
  357.     int err = Login(); //先登录  
  358.     if (err != 0)  
  359.     {  
  360.         return err; //错误代码必须要返回  
  361.     }  
  362.     if (FALSE == SendEmailHead()) //发送EMAIL头部信息  
  363.     {  
  364.         return 1; /*错误码1是由于网络的错误*/  
  365.     }  
  366.     if (FALSE == SendTextBody())  
  367.     {  
  368.         return 1; /*错误码1是由于网络的错误*/  
  369.     }  
  370.     err = SendAttachment_Ex();  
  371.     if (err != 0)  
  372.     {  
  373.         return err;  
  374.     }  
  375.     if (FALSE == SendEnd())  
  376.     {  
  377.         return 1; /*错误码1是由于网络的错误*/  
  378.     }  
  379.     return 0; /*0表示没有出错*/  
  380. }  
  381.   
  382. void CSmtp::AddAttachment(string &filePath) //添加附件  
  383. {  
  384.     FILEINFO *pFile = new FILEINFO;  
  385.     strcpy_s(pFile->filePath, filePath.c_str());  
  386.     const char *p = filePath.c_str();  
  387.     strcpy_s(pFile->fileName, p + filePath.find_last_of("\\") + 1);  
  388.     listFile.push_back(pFile);  
  389. }  
  390.   
  391. void CSmtp::DeleteAttachment(string &filePath) //删除附件  
  392. {  
  393.     list<FILEINFO *>::iterator pIter;  
  394.     for (pIter = listFile.begin(); pIter != listFile.end(); pIter++)  
  395.     {  
  396.         if (strcmp((*pIter)->filePath, filePath.c_str()) == 0)  
  397.         {  
  398.             FILEINFO *p = *pIter;  
  399.             listFile.remove(*pIter);  
  400.             delete p;  
  401.             break;  
  402.         }  
  403.     }  
  404. }  
  405.   
  406. void CSmtp::DeleteAllAttachment() /*删除所有的文件*/  
  407. {  
  408.     for (list<FILEINFO *>::iterator pIter = listFile.begin(); pIter != listFile.end(); )  
  409.     {  
  410.         FILEINFO *p = *pIter;  
  411.         pIter = listFile.erase(pIter);  
  412.         delete p;  
  413.     }  
  414. }  
  415.   
  416. void CSmtp::SetSrvDomain(string &domain)  
  417. {  
  418.     this->domain = domain;  
  419. }  
  420.   
  421. void CSmtp::SetUserName(string &user)  
  422. {  
  423.     this->user = user;  
  424. }  
  425.   
  426. void CSmtp::SetPass(string &pass)  
  427. {  
  428.     this->pass = pass;  
  429. }  
  430. void CSmtp::SetTargetEmail(string &targetAddr)  
  431. {  
  432.     this->targetAddr = targetAddr;  
  433. }  
  434. void CSmtp::SetEmailTitle(string &title)  
  435. {  
  436.     this->title = title;  
  437. }  
  438. void CSmtp::SetContent(string &content)  
  439. {  
  440.     this->content = content;  
  441. }  
  442. void CSmtp::SetPort(int port)  
  443. {  
  444.     this->port = port;  
  445. }  



   测试代码如下:

   main.cpp

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. #include "Smtp.h"  
  2. #include <iostream>  
  3. using namespace std;  
  4.   
  5. int main()  
  6. {  
  7.   
  8.     CSmtp smtp(  
  9.         25,                             /*smtp端口*/  
  10.         "smtp.163.com",                 /*smtp服务器地址*/  
  11.         "it_is_just_a_test@163.com",    /*你的邮箱地址*/  
  12.         "19930714lyh",                  /*邮箱密码*/  
  13.         "it_is_just_a_test@126.com",    /*目的邮箱地址*/  
  14.         "好啊!",                          /*主题*/  
  15.         "李懿虎同学,你好!收到请回复!"       /*邮件正文*/  
  16.         );  
  17.     //添加附件时注意,\一定要写成\\,因为转义字符的缘故  
  18.     string filePath("D:\\课程设计报告.doc");  
  19.     smtp.AddAttachment(filePath);  
  20.     /*还可以调用CSmtp::DeleteAttachment函数删除附件,还有一些函数,自己看头文件吧!*/  
  21.     //filePath = "C:\\Users\\李懿虎\\Desktop\\sendEmail.cpp";  
  22.     //smtp.AddAttachment(filePath);  
  23.   
  24.     int err;  
  25.     if ((err = smtp.SendEmail_Ex()) != 0)  
  26.     {  
  27.         if (err == 1)  
  28.             cout << "错误1: 由于网络不畅通,发送失败!" << endl;  
  29.         if (err == 2)  
  30.             cout << "错误2: 用户名错误,请核对!" << endl;  
  31.         if (err == 3)  
  32.             cout << "错误3: 用户密码错误,请核对!" << endl;  
  33.         if (err == 4)  
  34.             cout << "错误4: 请检查附件目录是否正确,以及文件是否存在!" << endl;  
  35.     }  
  36.     system("pause");  
  37.     return 0;  
  38. }  

        在VS2005下面,很有可能会出现带中文的目录或者中文名文件打不开的情况,这个时候这么解决:在两个构造函数里面的第一句加上:setlocale(LC_ALL,"Chinese-simplified");这一句话即可解决!

     在VS2013里面貌似这个程序可以运行,微软这朵奇葩!不说了!

     请尽量不使用QQ邮箱登陆,因为我试过,貌似连不上。qq邮箱的服务器返回告诉你要求安全的连接,用SSL什么的,哎,qq也是朵奇葩!



0 0
原创粉丝点击