项目需要做发邮件的功能,在网上找了一下代码,比较出名的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 = ; //send和recv的缓存buffer的size
const int SERVICE_PORT=; //端口 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) == )
{
//已经存在
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) == )
{
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();
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("\\")+,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(,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 = ;
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>> ];
out_str += _AppOctStrmContent_encode_chars[ (c1&0x3)<< ];
out_str += "==";
break;
} // read the second byte
c2 = in_str[i++];
if ( i==len ) // pad with "="
{
out_str += _AppOctStrmContent_encode_chars[ c1>> ];
out_str += _AppOctStrmContent_encode_chars[ ((c1&0x3)<<) | ((c2&0xF0)>>) ];
out_str += _AppOctStrmContent_encode_chars[ (c2&0xF)<< ];
out_str += "=";
break;
} // read the third byte
c3 = in_str[i++];
// convert into four bytes string
out_str += _AppOctStrmContent_encode_chars[ c1>> ];
out_str += _AppOctStrmContent_encode_chars[ ((c1&0x3)<<) | ((c2&0xF0)>>) ];
out_str += _AppOctStrmContent_encode_chars[ ((c2&0xF)<<) | ((c3&0xC0)>>) ];
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(), );
if ( len_s < )
{
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 = ;
char response_buffer[MAX_BUFFER_SIZE];
if ( (recv_bytes = recv(_socket, response_buffer, MAX_BUFFER_SIZE, )) < )
{
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(, ) != 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( , );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != )
{
m_logInfo.logInfo("WSAStartup调用失败!");
return false;
}
if ( LOBYTE( wsaData.wVersion ) != ||
HIBYTE( wsaData.wVersion ) != )
{
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,,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(""))
return false;
return true;
} bool CSendMail::Logon()
{
char local_host[MAX_BUFFER_SIZE];
if ( gethostname(local_host, MAX_BUFFER_SIZE) != )
{
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(""))
{
return false;
} msg = "AUTH LOGIN\r\n";
sendRequest(msg);
if(!rcvResponse(""))
{
return false;
} msg = Base64Encode(m_UserName) + "\r\n";
sendRequest(msg);
if(!rcvResponse(""))
{
return false;
} msg = Base64Encode(m_UserPwd) + "\r\n";
sendRequest(msg);
if(!rcvResponse(""))
{
return false;
} return true;//登录成功
} ///////////////////////////////////SendMail////////////////////////////////////////////////////
//发送邮件头
bool CSendMail::SendHead()
{
std::string msg; msg = "MAIL FROM:<";
msg += m_SenderAddr + ">\r\n";
sendRequest(msg);
if(!rcvResponse(""))
{
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(""))
{
return false;
}
} msg = "DATA\r\n";
sendRequest(msg);
if(!rcvResponse(""))
{
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=,len=szContent.length();
while(npos<len)
{
std::string szBuffer=Base64Encode(szContent.substr(npos,min(len-npos,)));
szBuffer+="\r\n";
sendRequest(szBuffer);
npos += min(len-npos,);
}
} 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=;
//实现输出类
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 ;
}
测试时,把
sMailer.setUserName("userName");
sMailer.setUserPwd("userPwd");
改了就可以发送邮件了。
附实现好的工程下载:http://download.csdn.net/detail/bian*gzi/7948843
参考:
比较出名也写得比较好的SMailer【一组实现邮件发送功能的C++封装类】:http://morningspace.51.net/resource/SMailer.php
实现逻辑抄自【C++发送邮件和附件】:http://blog.csdn.net/ybjx111/article/details/7031055#