實現了發郵件功能,根據設置不同可設置qq郵箱發送,或163郵箱發送。
源代碼:h文件
/*
**CSendMail頭文件
**實現郵件的發送功能,支持多個用戶接收,支持附件
**program by six_beauty
*/
#pragma once
#include <string>
#include <list>
#include <map>
#include "LogInfo.h"
#include <winsock2.h>
//類型定義
const int MAX_BUFFER_SIZE = 255; //send和recv的緩存buffer的size
typedef std::map<std::string, std::string> RECEIVERS;
//CSendMail類
class CSendMail
{
public:
CSendMail();
~CSendMail();
//////////////////////////////////////設置郵件信息/////////////////////////////////////////////////////////////////////////////////
/////////////////////////connent///////////////////////////////////
void setServerName(const std::string server_name); //smtp服務器地址
void setServerPort(int port); //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服務器地址
int m_Port; //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
/////////////////////////郵件信息///////////////////////////////////
};
源代碼: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::setServerPort(int port)
{
m_Port = port;
}
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)
{
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(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(m_Port);//發郵件一般都是25端口 qq郵箱587
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("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);
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;
}
源代碼:輸出(h文件,功能被注釋,沒來得及刪掉,需要加進去)
#include<iostream>
#include<stdarg.h>
using namespace std;
const int BUF_SIZE=4096;
//實現輸出類
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 ;
}
};
源代碼:測試代碼,這幾個參數自己賦值即可
CSendMail sMailer;
//郵箱smtp,如"smtp.126.com"
sMailer.setServerName(c_serverName);
//郵箱端口號,163端口25,qq端口587
sMailer.setServerPort(serverPort);
//郵箱賬號名,如"****@126.com"
sMailer.setUserName(c_userName);
//郵箱密碼:163郵箱為網易授權密碼,不是郵箱賬號對應的密碼 類似DGJNQYDJXTULMGLS
sMailer.setUserPwd(c_userPwd);
//發件人名字
sMailer.setSenderName("sender");
//發送郵箱地址,填你賬號的地址,上面的郵箱賬號名"****@163.com",即填賬號名
sMailer.setSenderAddress(c_userName);
//添加郵件接收者,可添加多個
sMailer.setReceiver("receiver", c_receiverAddress);
//添加附件
//sMailer.AddFilePath("F:\\mailfile\\out.txt");
//發送郵件
if (sMailer.Connent())//每次發郵件前都需要connect
{
//第一個字符串是郵件標題,第二個是郵件內容
if (sMailer.SendMail(c_head, c_content))
{
//郵件發送完成!
}
}
參考了https://www.cnblogs.com/sixbeauty/p/3983525.html
一開始用的163郵箱,試了多次,就發出去兩次(163往qq郵箱發),雖然每次都成功。后來換成qq,每次都能發過去(qq往163郵箱發)。不知道是不是網易郵箱有什么限制。
