項目須要作發郵件的功能,在網上找了一下代碼,比較出名的SMailer編譯不過(把那個Base64的encode拉到MailSender中實現就能過,但我搞不懂原來出錯的緣由,就不想用),另外找到了一個CSendMail的實現類,能夠用,但代碼的風格很差,使用起來也不方便,因此我就參考(chao)這兩個類,實現了一個比較簡單的發郵件類。CSendMail類支持多個收件人,支持附件(多個)。php
[原]:http://www.cnblogs.com/sixbeauty/p/3983525.htmlhtml
上代碼,頭文件:ios
/* **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 /////////////////////////郵件信息/////////////////////////////////// };
源文件:c++
/* **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) + "\r\n"; sendRequest(msg); if(!rcvResponse("250")) { return false; } msg = "AUTH LOGIN\r\n"; sendRequest(msg); if(!rcvResponse("334")) { return false; } msg = Base64Encode(m_UserName) + "\r\n"; sendRequest(msg); if(!rcvResponse("334")) { return false; } msg = Base64Encode(m_UserPwd) + "\r\n"; sendRequest(msg); if(!rcvResponse("235")) { return false; } return true;//登陸成功 } ///////////////////////////////////SendMail//////////////////////////////////////////////////// //發送郵件頭 bool CSendMail::SendHead() { std::string msg; msg = "MAIL FROM:<"; msg += m_SenderAddr + ">\r\n"; 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 + ">\r\n"; sendRequest(msg); if(!rcvResponse("250")) { return false; } } msg = "DATA\r\n"; sendRequest(msg); if(!rcvResponse("354")) { return false; } //發送Headers msg = "From:\"" + m_SenderName + "\"<" + m_SenderAddr + ">\r\n"; //遍歷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 += "\r\n"; msg += "Date: "; msg += prepareDate() + "\r\n"; msg += "Subject: "; msg += m_MailTitle + "\r\n"; msg += "X-Mailer: six_beauty \r\n"; msg += "MIME-Version: 1.0\r\n"; msg += "Content-type: multipart/mixed; boundary=\"INVT\"\r\n\r\n"; msg += "\r\n"; sendRequest(msg); return true; } bool CSendMail::SendTextBody() { std::string msg; msg = "--INVT\r\nContent-Type: text/plain;\r\n charset=\"gb2312\"\r\n\r\n"; msg += m_TextBody; msg += "\r\n\r\n"; 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\r\nContent-Type: application/octet-stream;\r\n name=\""; msg += fileName; msg += "\"\r\nContent-Transfer-Encoding: base64\r\nContent-Disposition: attachment;\r\n filename=\""; msg += fileName; msg += "\"\r\n\r\n"; sendRequest(msg,true); int npos=0,len=szContent.length(); while(npos<len) { std::string szBuffer=Base64Encode(szContent.substr(npos,min(len-npos,3000))); szBuffer+="\r\n"; sendRequest(szBuffer); npos += min(len-npos,3000); } } return true; } bool CSendMail::SendEnd() { std::string msg; msg = "--INVT--\r\n.\r\n"; sendRequest(msg,true); msg = "QUIT\r\n"; 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; }
測試時,把app
sMailer.setUserName("userName"); sMailer.setUserPwd("userPwd");
改了就能夠發送郵件了。curl
附實現好的工程下載:http://download.csdn.net/detail/biantaiwangzi/7948843socket
參考:函數
比較出名也寫得比較好的SMailer【一組實現郵件發送功能的C++封裝類】:http://morningspace.51.net/resource/SMailer.php
實現邏輯抄自【C++發送郵件和附件】:http://blog.csdn.net/ybjx111/article/details/7031055#