最近做项目需要用到发邮件的功能,于是去网上找了一个博主写的封装好的发邮件类,原文链接:https://www.cnblogs.com/sixbeauty/p/3983525.html。可以实现同时给多人发送、同时发送多封邮件、发送多个附件。
拷贝源代码加进工程之后会有一些函数不能再用了,下面给出改了那些之后的代码。
源码
LogInfo.h文件
#pragma once
#include<iostream>
#include<stdarg.h>
using namespace std;
const int BUF_SIZE = 4096;
//实现输出类
class LogInfo
{
public:
LogInfo() {};
~LogInfo() {};
void logInfo(const char *szFormat, ...)
{
char szBuf[BUF_SIZE] = {};
va_list args; //第一步
va_start(args, szFormat); //第二步
//_vsnprintf(szBuf, BUF_SIZE, szFormat, args); //第三步
-_vsnprintf_s(szBuf, BUF_SIZE, szFormat, args);
va_end(args); //第四步
//在这是实现输出方式
std::cout << szBuf << endl;
return;
}
};
CSendMail.h
#pragma once
/*
**CSendMail头文件
**实现邮件的发送功能,支持多个用户接收,支持附件
**program by six_beauty
*/
#include <string>
#include <list>
#include <map>
#include "LogInfo.h"
#include <winsock2.h>
#include<winsock.h>
#pragma comment(lib,"ws2_32.lib")
//类型定义
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.cpp
/*
**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)
{
//-------------------------------here
//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
tm tms;
localtime_s(&tms, &seconds);
strftime(date_string, MAX_BUFFER_SIZE, "%a, %d %b %y %H:%M:%S +0800", &tms);
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());//使用名称
//GetAddrInfoW
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);
cout << "before 354" << endl;
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;
}
main.cpp
#include "CSendMail.h"
#include "iostream"
using namespace std;
int main()
{
CSendMail sMailer;
// freopen("F://mailfile//out.txt","w+",stdout);
sMailer.setServerName("smtp.qq.com"); //邮箱smtp,如"smtp.126.com"
sMailer.setUserName("xxx@qq.com"); //邮箱账号名,如"****@126.com"
sMailer.setUserPwd("xxx"); //邮箱密码
sMailer.setSenderName("w"); //发件人名字
sMailer.setSenderAddress("xxx@qq.com"); //发送邮箱地址,填你账号的地址,上面的邮箱账号名"****@126.com",同上面的邮箱账号名
sMailer.setReceiver("y", "xxx@qq.com"); //添加邮件接收者
sMailer.addReceiver("h", "xxx@qq.com");
sMailer.addReceiver("w", "xxx@qq.com");
sMailer.AddFilePath("C:\\Users\\LucyGaler\\Desktop\\output.xlsx"); //添加附件
// sMailer.AddFilePath("F:/mailfile/libcurl.exp"); //添加附件
//发送第一份邮件
if (sMailer.Connent()) //每次发邮件前都需要connect
{
if (sMailer.SendMail("tell you something", "hello beauty")) //第一个字符串是邮件标题,第二个是邮件内容
cout << "邮件发送完成!";
}
//发送第二份邮件
if (sMailer.Connent()) //每次发邮件前都需要connect
{
std::string title, content;
title = "I have to say";
content = "a little happy\n heihei";
if (sMailer.SendMail(title, content)) //第一个字符串是邮件标题,第二个是邮件内容
cout << "邮件发送完成!";
}
int a;
cin >> a;
return 0;
}
使用
要使用的话把main.cpp里面的邮箱什么的改对就行了。
sMailer.setServerName("smtp.qq.com");
sMailer.setUserName("xxx@qq.com"); //发送方的邮箱地址
sMailer.setUserPwd("xxx"); //发送方邮箱密码
sMailer.setSenderName("w"); //发送方名字
sMailer.setSenderAddress("xxx@qq.com"); //发送方邮箱地址
sMailer.setReceiver("y", "xxx@qq.com"); //设置接收方:名字,邮箱地址
sMailer.addReceiver("h", "xxx@qq.com"); //可以添加任意个
sMailer.addReceiver("w", "xxx@qq.com");
sMailer.AddFilePath("C:\\Users\\LucyGaler\\Desktop\\output.xlsx"); //附件路径
第一个setServerName是邮箱的smtp,下面给出常用的smtp
常用SMTP地址
1、QQ邮箱(mail.qq.com)
POP3服务器地址:pop.qq.com(端口:110)
SMTP服务器地址:smtp.qq.com(端口:25)
2、搜狐邮箱(sohu.com):
POP3服务器地址:pop3.sohu.com(端口:110)
SMTP服务器地址:smtp.sohu.com(端口:25)
3、HotMail邮箱(hotmail.com):
POP3服务器地址:pop.live.com(端口:995)
SMTP服务器地址:smtp.live.com(端口:587)
4、移动139邮箱:
POP3服务器地址:POP.139.com(端口:110)
SMTP服务器地址:SMTP.139.com(端口:25)
5、景安网络邮箱:
POP3服务器地址:POP.zzidc.com(端口:110)
SMTP服务器地址:SMTP.zzidc.com(端口:25)
我测试的时候用的是QQ邮箱所以smtp是smtp.qq.com。
运行时的问题之一
报错:Use getaddrinfo() or GetAddrInfoW() instead or define _WINSOCK_DEPRECATED_NO_WARNINGS to disable deprecated API warnings
解决办法参照https://www.cnblogs.com/zq10/p/10640322.html