首页 > 代码库 > 邮件正文及其附件的发送的C++实现

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

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

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

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

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

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

   Smtp.h

#include <WinSock2.h>
#include <iostream>
#include <string>
#include <list>
using namespace std;

#pragma  comment(lib, "ws2_32.lib")	/*链接ws2_32.lib动态链接库*/
const int MAXLEN = 1024;
const int MAX_FILE_LEN = 6000;

static const char base64Char[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

struct FILEINFO /*用来记录文件的一些信息*/
{
	char fileName[128]; /*文件名称*/
	char filePath[256]; /*文件绝对路径*/
};

class CSmtp
{
public:
	CSmtp(void);
	CSmtp(
		int port,
		string srvDomain,	//smtp服务器域名
		string userName,	//用户名
		string password,	//密码
		string targetEmail, //目的邮件地址
		string emailTitle,  //主题
		string content       //内容
		);
public:
	~CSmtp(void);
public:
	int port;
public:
	string domain;
	string user;
	string pass;
	string targetAddr;
	string title;
	string content;
	/*为了方便添加文件,删除文件神马的,使用list容器最为方便,相信大家在数据结构里面都学过*/
	list <FILEINFO *> listFile;
	char *fileData;
public:
	char buff[MAXLEN + 1];
	int buffLen;
	SOCKET sockClient;	//客户端的套接字
public:
	SOCKET CreateConn(); /*创建连接*/

	BOOL Send(string &message);
	BOOL Recv();
	int GetStrLen(char* pString); //得到字符串的长度

	void FormatEmailHead(string &email);//格式化要发送的邮件头部
	int Login();
	BOOL SendEmailHead();		//发送邮件头部信息
	BOOL SendTextBody();	    //发送文本信息
	//BOOL SendAttachment();	    //发送附件
	BOOL SendAttachment_Ex();
	BOOL SendEnd();
	int SendEmail();
public:
	void AddAttachment(string &filePath); //添加附件
	void DeleteAttachment(string &filePath); //删除附件
	void DeleteAllAttachment(); //删除所有的附件
	int SendEmail_Ex();
	char* base64Encode(char const* origSigned, unsigned origLength);
};
Smtp.cpp

#include "Smtp.h"
#include <iostream>
#include <fstream>
using namespace std;

/*base64采用别人的编码,不过,这不是重点,重点是我完成了我的一个比较好的邮件发送客户端*/
char* CSmtp::base64Encode(char const* origSigned, unsigned origLength)
{
	unsigned char const* orig = (unsigned char const*)origSigned; // in case any input bytes have the MSB set
	if (orig == NULL) return NULL;

	unsigned const numOrig24BitValues = origLength / 3;
	bool havePadding = origLength > numOrig24BitValues * 3;
	bool havePadding2 = origLength == numOrig24BitValues * 3 + 2;
	unsigned const numResultBytes = 4 * (numOrig24BitValues + havePadding);
	char* result = new char[numResultBytes + 3]; // allow for trailing '/0'

	// Map each full group of 3 input bytes into 4 output base-64 characters:
	unsigned i;
	for (i = 0; i < numOrig24BitValues; ++i)
	{
		result[4 * i + 0] = base64Char[(orig[3 * i] >> 2) & 0x3F];
		result[4 * i + 1] = base64Char[(((orig[3 * i] & 0x3) << 4) | (orig[3 * i + 1] >> 4)) & 0x3F];
		result[4 * i + 2] = base64Char[((orig[3 * i + 1] << 2) | (orig[3 * i + 2] >> 6)) & 0x3F];
		result[4 * i + 3] = base64Char[orig[3 * i + 2] & 0x3F];
	}

	// Now, take padding into account.  (Note: i == numOrig24BitValues)
	if (havePadding)
	{
		result[4 * i + 0] = base64Char[(orig[3 * i] >> 2) & 0x3F];
		if (havePadding2)
		{
			result[4 * i + 1] = base64Char[(((orig[3 * i] & 0x3) << 4) | (orig[3 * i + 1] >> 4)) & 0x3F];
			result[4 * i + 2] = base64Char[(orig[3 * i + 1] << 2) & 0x3F];
		}
		else
		{
			result[4 * i + 1] = base64Char[((orig[3 * i] & 0x3) << 4) & 0x3F];
			result[4 * i + 2] = '=';
		}
		result[4 * i + 3] = '=';
	}

	result[numResultBytes] = '\0';
	return result;
}
CSmtp::CSmtp(void)
{
	this->content = "";
	this->port = 0;
	this->user = "";
	this->pass = "";
	this->targetAddr = "";
	this->title = "";
	this->domain = "";
	this->sockClient = 0;

}

CSmtp::~CSmtp(void)
{
	delete []fileData;
	closesocket(sockClient);
	WSACleanup();
}


CSmtp::CSmtp(
			 int port,
			 string srvDomain, 
			 string userName, 
			 string password, 
			 string targetEmail,
			 string emailTitle, 
			 string content
			 )
{
	this->content = content;
	this->port = port;
	this->user = userName;
	this->pass = password;
	this->targetAddr = targetEmail;
	this->title = emailTitle;
	this->domain = srvDomain;

	WORD wVersionRequested;
	WSADATA wsaData;
	int err;
	wVersionRequested = MAKEWORD(2, 1);
	err = WSAStartup(wVersionRequested, &wsaData);
	this->sockClient = CreateConn(); //创建连接
}

SOCKET CSmtp::CreateConn()
{
	//为建立socket对象做准备,初始化环境
	SOCKET sockClient = socket(AF_INET,SOCK_STREAM,0); //建立socket对象
	SOCKADDR_IN addrSrv; 
	HOSTENT* pHostent;
	pHostent = gethostbyname(domain.c_str());  //得到有关于域名的信息

	addrSrv.sin_addr.S_un.S_addr = *((DWORD *)pHostent->h_addr_list[0]);	//得到smtp服务器的网络字节序的ip地址   
	addrSrv.sin_family = AF_INET;   
	addrSrv.sin_port = htons(port);   
	int err = connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));   //向服务器发送请求  
	if(err != 0)
	{
		return 0;
		//printf("链接失败\n");
	}
	return sockClient;
}

BOOL CSmtp::Send(string &message)
{
	int err = send(sockClient, message.c_str(), message.length(), 0);
	if (err == SOCKET_ERROR)
	{
		return FALSE;
	}
	cout << message << endl;
	return TRUE;
}

BOOL CSmtp::Recv() 
{
	memset(buff, 0, sizeof(char)* (MAXLEN + 1));
	int err = recv(sockClient, buff, MAXLEN, 0); //接收数据
	if (err == SOCKET_ERROR)
	{
		return FALSE;
	}
	buff[err] = '\0';
	cout << buff << endl;
	return TRUE;
}

int CSmtp::Login()
{
	Recv();

	string sendBuff;
	sendBuff = "EHLO ";
	sendBuff += user;
	sendBuff += "\r\n";

	if (FALSE == Send(sendBuff) || FALSE == Recv()) //既接收也发送
	{
		return 1; /*1表示发送失败由于网络错误*/
	}

	sendBuff.empty();
	sendBuff = "AUTH LOGIN\r\n";
	if (FALSE == Send(sendBuff) || FALSE == Recv()) //请求登陆
	{
		return 1; /*1表示发送失败由于网络错误*/
	}

	sendBuff.empty();
	int pos = user.find('@', 0);
	sendBuff = user.substr(0, pos); //得到用户名

	char *ecode;
	/*在这里顺带扯一句,关于string类的length函数与C语言中的strlen函数的区别,strlen计算出来的长度,只到'\0'字符为止,而string::length()函数实际上返回的是string类中字符数组的大小,你自己可以测试一下,这也是为什么我下面不使用string::length()的原因*/

	ecode = base64Encode(sendBuff.c_str(), strlen(sendBuff.c_str()));
	sendBuff.empty();
	sendBuff = ecode;
	sendBuff += "\r\n";
	delete []ecode;

	if (FALSE == Send(sendBuff) || FALSE == Recv()) //发送用户名,并接收服务器的返回
	{
		return 1; /*错误码1表示发送失败由于网络错误*/
	}

	sendBuff.empty();
	ecode = base64Encode(pass.c_str(), strlen(pass.c_str()));
	sendBuff = ecode;
	sendBuff += "\r\n";
	delete []ecode;

	if (FALSE == Send(sendBuff) || FALSE == Recv()) //发送用户密码,并接收服务器的返回
	{
		return 1; /*错误码1表示发送失败由于网络错误*/
	}

	if (NULL != strstr(buff, "550"))
	{
		return 2;/*错误码2表示用户名错误*/
	}

	if (NULL != strstr(buff, "535")) /*535是认证失败的返回*/
	{
		return 3; /*错误码3表示密码错误*/
	}
	return 0;
}

BOOL CSmtp::SendEmailHead()		//发送邮件头部信息
{
	string sendBuff;
	sendBuff = "MAIL FROM: <" + user + ">\r\n";
	if (FALSE == Send(sendBuff) || FALSE == Recv())
	{
		return FALSE; /*表示发送失败由于网络错误*/
	}


	sendBuff.empty();
	sendBuff = "RCPT TO: <" + targetAddr + ">\r\n";
	if (FALSE == Send(sendBuff) || FALSE == Recv())
	{
		return FALSE; /*表示发送失败由于网络错误*/
	}

	sendBuff.empty();
	sendBuff = "DATA\r\n";
	if (FALSE == Send(sendBuff) || FALSE == Recv())
	{
		return FALSE; //表示发送失败由于网络错误
	}

	sendBuff.empty();
	FormatEmailHead(sendBuff);
	if (FALSE == Send(sendBuff)) 
//发送完头部之后不必调用接收函数,因为你没有\r\n.\r\n结尾,服务器认为你没有发完数据,所以不会返回什么值
	{
		return FALSE; /*表示发送失败由于网络错误*/
	}
	return TRUE;
}

void CSmtp::FormatEmailHead(string &email)
{/*格式化要发送的内容*/
	email = "From: ";
	email += user;
	email += "\r\n";

	email += "To: ";
	email += targetAddr;
	email += "\r\n";

	email += "Subject: ";
	email += title;
	email += "\r\n";

	email += "MIME-Version: 1.0";
	email += "\r\n";

	email += "Content-Type: multipart/mixed;boundary=qwertyuiop";
	email += "\r\n";
	email += "\r\n";
}

BOOL CSmtp::SendTextBody()  /*发送邮件文本*/
{
	string sendBuff;
	sendBuff = "--qwertyuiop\r\n";
	sendBuff += "Content-Type: text/plain;";
	sendBuff += "charset=\"gb2312\"\r\n\r\n";
	sendBuff += content;
	sendBuff += "\r\n\r\n";
	return Send(sendBuff);
}

BOOL CSmtp::SendAttachment_Ex() /*发送附件*/
{
	for (list<FILEINFO *>::iterator pIter = listFile.begin(); pIter != listFile.end(); pIter++)
	{
		cout << "Attachment is sending ~~~~~" << endl;
		cout << "Please be patient!" << endl;
		string sendBuff;
		sendBuff = "--qwertyuiop\r\n";
		sendBuff += "Content-Type: application/octet-stream;\r\n";
		sendBuff += " name=\"";
		sendBuff += "base64.cpp\"";
		sendBuff += "\r\n";

		sendBuff += "Content-Transfer-Encoding: base64\r\n";
		sendBuff += "Content-Disposition: attachment;\r\n";
		sendBuff += " filename=\"";
		sendBuff += (*pIter)->fileName;
		sendBuff += "\"";

		sendBuff += "\r\n";
		sendBuff += "\r\n";
		Send(sendBuff);
		ifstream ifs((*pIter)->filePath, ios::in | ios::binary);

		char fileBuff[MAX_FILE_LEN];
		char *chSendBuff;
		memset(fileBuff, 0, sizeof(fileBuff));
		/*文件使用base64加密传送*/
		while (ifs.read(fileBuff, MAX_FILE_LEN))
		{
			//cout << ifs.gcount() << endl;
			chSendBuff = base64Encode(fileBuff, MAX_FILE_LEN);
			chSendBuff[strlen(chSendBuff)] = '\r';
			chSendBuff[strlen(chSendBuff)] = '\n';
			send(sockClient, chSendBuff, strlen(chSendBuff), 0);
			delete[]chSendBuff;
		}
		//cout << ifs.gcount() << endl;
		chSendBuff = base64Encode(fileBuff, ifs.gcount());
		chSendBuff[strlen(chSendBuff)] = '\r';
		chSendBuff[strlen(chSendBuff)] = '\n';
		int err = send(sockClient, chSendBuff, strlen(chSendBuff), 0);
	
		if (err != strlen(chSendBuff))
		{
			cout << "文件传送出错!" << endl;
			return FALSE;
		}
		delete []chSendBuff;
	}
	return TRUE;
}

BOOL CSmtp::SendEnd() /*发送结尾信息*/
{
	string sendBuff;
	sendBuff = "--qwertyuiop--";
	sendBuff += "\r\n.\r\n";
	if (FALSE == Send(sendBuff) || FALSE == Recv())
	{
		return FALSE;
	}
	cout << buff << endl;
	sendBuff.empty();
	sendBuff = "QUIT\r\n";
	return (Send(sendBuff) && Recv());
}

int CSmtp::SendEmail_Ex()
{
	int err = Login(); //先登录
	if (err != 0)
	{
		return err; //错误代码必须要返回
	}
	if (FALSE == SendEmailHead()) //发送EMAIL头部信息
	{
		return 1; /*错误码1是由于网络的错误*/
	}
	if (FALSE == SendTextBody())
	{
		return 1; /*错误码1是由于网络的错误*/
	}
	if (FALSE == SendAttachment_Ex())
	{
		return 1; /*错误码1是由于网络的错误*/
	}
	if (FALSE == SendEnd())
	{
		return 1; /*错误码1是由于网络的错误*/
	}
	return 0; /*0表示没有出错*/
}

void CSmtp::AddAttachment(string &filePath) //添加附件
{
	FILEINFO *pFile = new FILEINFO;
	strcpy_s(pFile->filePath, filePath.c_str());
	const char *p = filePath.c_str();
	strcpy_s(pFile->fileName, p + filePath.find_last_of("\\") + 1);
	listFile.push_back(pFile);
}

void CSmtp::DeleteAttachment(string &filePath) //删除附件
{
	list<FILEINFO *>::iterator pIter;
	for (pIter = listFile.begin(); pIter != listFile.end(); pIter++)
	{
		if (strcmp((*pIter)->filePath, filePath.c_str()) == 0)
		{
			listFile.remove(*pIter);
			delete *pIter;
			break;
		}
	}
}

void CSmtp::DeleteAllAttachment() /*删除所有的文件*/
{
	for (list<FILEINFO *>::iterator pIter = listFile.begin(); pIter != listFile.end(); pIter++)
	{
		listFile.remove(*pIter);
		delete *pIter;
	}
}

    测试代码如下:

main.cpp

#include "Smtp.h"
#include <iostream>
using namespace std;

int main()
{

	CSmtp smtp(
		25,								/*smtp端口*/
		"smtp.163.com",					/*smtp服务器地址*/
		"it_is_just_a_test@163.com",	/*你的邮箱地址*/
		"19930714lyh",					/*邮箱密码*/
		"it_is_just_a_test@126.com",	/*目的邮箱地址*/
		"好啊!",							/*主题*/
		"李懿虎同学,你好!收到请回复!"		/*邮件正文*/
		);
	//添加附件时注意,\一定要写成\\,因为转义字符的缘故
	string filePath("C:\\Users\\李懿虎\\Desktop\\App\\SAkura\\模版.png");
	smtp.AddAttachment(filePath);
	/*还可以调用CSmtp::DeleteAttachment函数删除附件,还有一些函数,自己看头文件吧!*/
	filePath = "C:\\Users\\李懿虎\\Desktop\\sendEmail.cpp";
	smtp.AddAttachment(filePath);

	smtp.SendEmail_Ex();
	system("pause");
	return 0;
}