zoukankan      html  css  js  c++  java
  • 邮件正文及其附件的发送的C++实现

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

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

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

       网上非常多发送邮件附件的代码都不能用,所以我用心写了一个,直接封装成了一个类,须要的同学能够直接调用这个类来发送邮件,纯c++代码。(在VS2013下測试完美通过!)

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

       Smtp.h

    #ifndef __SMTP_H__ //避免反复包括
    #define __SMTP_H__
    
    #include <iostream>
    #include <list>
    #include <WinSock2.h>
    using namespace std;
    
    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,	//smtpserver域名
    		string userName,	//username
    		string password,	//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;
    
    public:
    	char buff[MAXLEN + 1];
    	int buffLen;
    	SOCKET sockClient;	//client的套接字
    public:
    	bool CreateConn(); /*创建连接*/
    
    	bool Send(string &message);
    	bool Recv();
    
    	void FormatEmailHead(string &email);//格式化要发送的邮件头部
    	int Login();
    	bool SendEmailHead();		//发送邮件头部信息
    	bool SendTextBody();	    //发送文本信息
    	//bool SendAttachment();	    //发送附件
    	int SendAttachment_Ex();
    	bool SendEnd();
    public:
    	void AddAttachment(string &filePath); //加入附件
    	void DeleteAttachment(string &filePath); //删除附件
    	void DeleteAllAttachment(); //删除全部的附件
    
    	void SetSrvDomain(string &domain);
    	void SetUserName(string &user);
    	void SetPass(string &pass);
    	void SetTargetEmail(string &targetAddr);
    	void SetEmailTitle(string &title);
    	void SetContent(string &content);
    	void SetPort(int port);
    	int SendEmail_Ex();
    	/*关于错误码的说明:1.网络错误导致的错误2.username错误3.password错误4.文件不存在0.成功*/
    	char* base64Encode(char const* origSigned, unsigned origLength);
    };
    
    #endif // !__SMTP_H__


         Smtp.cpp

    #include "Smtp.h"
    #include <iostream>
    #include <fstream>
    using namespace std;
    
    #pragma  comment(lib, "ws2_32.lib")	/*链接ws2_32.lib动态链接库*/
    
    /*base64採用别人的编码,只是,这不是重点,重点是我完毕了我的一个比較好的邮件发送client*/
    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] = '';
    	return result;
    }
    CSmtp::CSmtp(void)
    {
    	this->content = "";
    	this->port = 25;
    	this->user = "";
    	this->pass = "";
    	this->targetAddr = "";
    	this->title = "";
    	this->domain = "";
    
    	WORD wVersionRequested;
    	WSADATA wsaData;
    	int err;
    	wVersionRequested = MAKEWORD(2, 1);
    	err = WSAStartup(wVersionRequested, &wsaData);
    	this->sockClient = 0;
    
    }
    
    CSmtp::~CSmtp(void)
    {
    	DeleteAllAttachment();
    	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 = 0;
    }
    
    bool 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]);	//得到smtpserver的网络字节序的ip地址   
    	addrSrv.sin_family = AF_INET;
    	addrSrv.sin_port = htons(port);
    	int err = connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));   //向server发送请求 
    	if (err != 0)
    	{
    		return false;
    		//printf("链接失败
    ");
    	}
    	this->sockClient = sockClient;
    	if (false == Recv())
    	{
    		return false;
    	}
    	return true;
    }
    
    bool CSmtp::Send(string &message)
    {
    	int err = send(sockClient, message.c_str(), message.length(), 0);
    	if (err == SOCKET_ERROR)
    	{
    		return false;
    	}
    	string message01;
    	cout << message.c_str() << 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] = '';
    	cout << buff << endl;
    	return true;
    }
    
    int CSmtp::Login()
    {
    	string sendBuff;
    	sendBuff = "EHLO ";
    	sendBuff += user;
    	sendBuff += "
    ";
    
    	if (false == Send(sendBuff) || false == Recv()) //既接收也发送
    	{
    		return 1; /*1表示发送失败因为网络错误*/
    	}
    
    	sendBuff.empty();
    	sendBuff = "AUTH LOGIN
    ";
    	if (false == Send(sendBuff) || false == Recv()) //请求登陆
    	{
    		return 1; /*1表示发送失败因为网络错误*/
    	}
    
    	sendBuff.empty();
    	int pos = user.find('@', 0);
    	sendBuff = user.substr(0, pos); //得到username
    
    	char *ecode;
    	/*在这里顺带扯一句,关于string类的length函数与C语言中的strlen函数的差别,strlen计算出来的长度,仅仅到''字符为止,而string::length()函数实际上返回的是string类中字符数组的大小,你自己能够測试一下。这也是为什么我以下不使用string::length()的原因*/
    
    	ecode = base64Encode(sendBuff.c_str(), strlen(sendBuff.c_str()));
    	sendBuff.empty();
    	sendBuff = ecode;
    	sendBuff += "
    ";
    	delete[]ecode;
    
    	if (false == Send(sendBuff) || false == Recv()) //发送username,并接收server的返回
    	{
    		return 1; /*错误码1表示发送失败因为网络错误*/
    	}
    
    	sendBuff.empty();
    	ecode = base64Encode(pass.c_str(), strlen(pass.c_str()));
    	sendBuff = ecode;
    	sendBuff += "
    ";
    	delete[]ecode;
    
    	if (false == Send(sendBuff) || false == Recv()) //发送用户密码,并接收server的返回
    	{
    		return 1; /*错误码1表示发送失败因为网络错误*/
    	}
    
    	if (NULL != strstr(buff, "550"))
    	{
    		return 2;/*错误码2表示username错误*/
    	}
    
    	if (NULL != strstr(buff, "535")) /*535是认证失败的返回*/
    	{
    		return 3; /*错误码3表示密码错误*/
    	}
    	return 0;
    }
    
    bool CSmtp::SendEmailHead()		//发送邮件头部信息
    {
    	string sendBuff;
    	sendBuff = "MAIL FROM: <" + user + ">
    ";
    	if (false == Send(sendBuff) || false == Recv())
    	{
    		return false; /*表示发送失败因为网络错误*/
    	}
    
    
    	sendBuff.empty();
    	sendBuff = "RCPT TO: <" + targetAddr + ">
    ";
    	if (false == Send(sendBuff) || false == Recv())
    	{
    		return false; /*表示发送失败因为网络错误*/
    	}
    
    	sendBuff.empty();
    	sendBuff = "DATA
    ";
    	if (false == Send(sendBuff) || false == Recv())
    	{
    		return false; //表示发送失败因为网络错误
    	}
    
    	sendBuff.empty();
    	FormatEmailHead(sendBuff);
    	if (false == Send(sendBuff))
    		//发送完头部之后不必调用接收函数,因为你没有
    .
    结尾,server觉得你没有发完数据,所以不会返回什么值
    	{
    		return false; /*表示发送失败因为网络错误*/
    	}
    	return true;
    }
    
    void CSmtp::FormatEmailHead(string &email)
    {/*格式化要发送的内容*/
    	email = "From: ";
    	email += user;
    	email += "
    ";
    
    	email += "To: ";
    	email += targetAddr;
    	email += "
    ";
    
    	email += "Subject: ";
    	email += title;
    	email += "
    ";
    
    	email += "MIME-Version: 1.0";
    	email += "
    ";
    
    	email += "Content-Type: multipart/mixed;boundary=qwertyuiop";
    	email += "
    ";
    	email += "
    ";
    }
    
    bool CSmtp::SendTextBody()  /*发送邮件文本*/
    {
    	string sendBuff;
    	sendBuff = "--qwertyuiop
    ";
    	sendBuff += "Content-Type: text/plain;";
    	sendBuff += "charset="gb2312"
    
    ";
    	sendBuff += content;
    	sendBuff += "
    
    ";
    	return Send(sendBuff);
    }
    
    int 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
    ";
    		sendBuff += "Content-Type: application/octet-stream;
    ";
    		sendBuff += " name="";
    		sendBuff += (*pIter)->fileName;
    		sendBuff += """;
    		sendBuff += "
    ";
    
    		sendBuff += "Content-Transfer-Encoding: base64
    ";
    		sendBuff += "Content-Disposition: attachment;
    ";
    		sendBuff += " filename="";
    		sendBuff += (*pIter)->fileName;
    		sendBuff += """;
    
    		sendBuff += "
    ";
    		sendBuff += "
    ";
    		Send(sendBuff);
    		ifstream ifs((*pIter)->filePath, ios::in | ios::binary);
    		if (false == ifs.is_open())
    		{
    			return 4; /*错误码4表示文件打开错误*/
    		}
    		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)] = '
    ';
    			chSendBuff[strlen(chSendBuff)] = '
    ';
    			send(sockClient, chSendBuff, strlen(chSendBuff), 0);
    			delete[]chSendBuff;
    		}
    		//cout << ifs.gcount() << endl;
    		chSendBuff = base64Encode(fileBuff, ifs.gcount());
    		chSendBuff[strlen(chSendBuff)] = '
    ';
    		chSendBuff[strlen(chSendBuff)] = '
    ';
    		int err = send(sockClient, chSendBuff, strlen(chSendBuff), 0);
    
    		if (err != strlen(chSendBuff))
    		{
    			cout << "文件传送出错!" << endl;
    			return 1;
    		}
    		delete[]chSendBuff;
    	}
    	return 0;
    }
    
    bool CSmtp::SendEnd() /*发送结尾信息*/
    {
    	string sendBuff;
    	sendBuff = "--qwertyuiop--";
    	sendBuff += "
    .
    ";
    	if (false == Send(sendBuff) || false == Recv())
    	{
    		return false;
    	}
    	cout << buff << endl;
    	sendBuff.empty();
    	sendBuff = "QUIT
    ";
    	return (Send(sendBuff) && Recv());
    }
    
    int CSmtp::SendEmail_Ex()
    {
    	if (false == CreateConn())
    	{
    		return 1;
    	}
    	//Recv();
    	int err = Login(); //先登录
    	if (err != 0)
    	{
    		return err; //错误代码必需要返回
    	}
    	if (false == SendEmailHead()) //发送EMAIL头部信息
    	{
    		return 1; /*错误码1是因为网络的错误*/
    	}
    	if (false == SendTextBody())
    	{
    		return 1; /*错误码1是因为网络的错误*/
    	}
    	err = SendAttachment_Ex();
    	if (err != 0)
    	{
    		return err;
    	}
    	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)
    		{
    			FILEINFO *p = *pIter;
    			listFile.remove(*pIter);
    			delete p;
    			break;
    		}
    	}
    }
    
    void CSmtp::DeleteAllAttachment() /*删除全部的文件*/
    {
    	for (list<FILEINFO *>::iterator pIter = listFile.begin(); pIter != listFile.end();)
    	{
    		FILEINFO *p = *pIter;
    		pIter = listFile.erase(pIter);
    		delete p;
    	}
    }
    
    void CSmtp::SetSrvDomain(string &domain)
    {
    	this->domain = domain;
    }
    
    void CSmtp::SetUserName(string &user)
    {
    	this->user = user;
    }
    
    void CSmtp::SetPass(string &pass)
    {
    	this->pass = pass;
    }
    void CSmtp::SetTargetEmail(string &targetAddr)
    {
    	this->targetAddr = targetAddr;
    }
    void CSmtp::SetEmailTitle(string &title)
    {
    	this->title = title;
    }
    void CSmtp::SetContent(string &content)
    {
    	this->content = content;
    }
    void CSmtp::SetPort(int port)
    {
    	this->port = port;
    }

       測试代码例如以下:

       main.cpp

    #include "Smtp.h"
    #include <iostream>
    using namespace std;
    
    int main()
    {
    
    	CSmtp smtp(
    		25,								/*smtpport*/
    		"smtp.163.com",					/*smtpserver地址*/
    		"it_is_just_a_test@163.com",	/*你的邮箱地址*/
    		"XXXXXXX",					/*邮箱password*/
    		"it_is_just_a_test@126.com",	/*目的邮箱地址*/
    		"好啊!",							/*主题*/
    		"XXX同学,你好。收到请回复!"		/*邮件正文*/
    		);
    	/**
    	//加入附件时注意,一定要写成\,由于转义字符的缘故
    	string filePath("D:\课程设计报告.doc");
    	smtp.AddAttachment(filePath);
    	*/
    	
    	/*还能够调用CSmtp::DeleteAttachment函数删除附件,另一些函数,自己看头文件吧!*/
    	//filePath = "C:\Users\李懿虎\Desktop\sendEmail.cpp";
    	//smtp.AddAttachment(filePath);
    
    	int err;
    	if ((err = smtp.SendEmail_Ex()) != 0)
    	{
    		if (err == 1)
    			cout << "错误1: 由于网络不畅通,发送失败!" << endl;
    		if (err == 2)
    			cout << "错误2: username错误,请核对!" << endl;
    		if (err == 3)
    			cout << "错误3: 用户password错误,请核对!" << endl;
    		if (err == 4)
    			cout << "错误4: 请检查附件文件夹是否正确,以及文件是否存在!" << endl;
    	}
    	system("pause");
    	return 0;
    }

            在VS2005以下,非常有可能会出现带中文的文件夹或者中文名文件打不开的情况。这个时候这么解决:在两个构造函数里面的第一句加上:setlocale(LC_ALL,"Chinese-simplified");这一句话就可以解决!

         在VS2013里面貌似这个程序能够执行。微软这朵奇葩!

    不说了!

         请尽量不使用QQ邮箱登陆,由于我试过。貌似连不上。qq邮箱的server返回告诉你要求安全的连接,用SSL什么的。哎。qq也是朵奇葩。

         附上我的一个project:https://github.com/lishuhuakai/Mail


  • 相关阅读:
    同步类容器和并发类容器
    T4模板生成自定义的实体类
    C# UrlEncode 编码
    PLSQL快速生成增删改查语句
    Oracle 检查星期只能是1-7的数字不能重复
    PLSQL 插入数据无响应
    C# DataTable 排序
    C# 获取程序集信息
    C# 调用WinRAR解压缩文件
    DataTable 获取一列最大值并修改
  • 原文地址:https://www.cnblogs.com/tlnshuju/p/6823290.html
Copyright © 2011-2022 走看看