C++发邮件

C++发邮件

最近做项目需要用到发邮件的功能,于是去网上找了一个博主写的封装好的发邮件类,原文链接: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

  • 0
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值