0、有关TLS和SSL
SSL/TLS是用来加密邮件流的。如果不加密的话很容易被人破解。
不过既然是要发送广告邮件,这个就不用在意了,使用没加密的就行。
另外在使用的时候,发现,qq的邮箱需要使用SSL才能连接上,这个再以后需要用qq发群邮件的时候再来处理,这里暂时只是使用163的邮箱,作为发送端
1、主要的类
SendMail.h:
/*********************************************************************** *发送邮件模块头文件 *可以发送文本和附件(支持多个附件一起发送) *************************************************************************/ #ifndef __INCLUDE_SEND_MAIL_H__ #define __INCLUDE_SEND_MAIL_H__ #include "vector" using namespace std; #include "atlenc.h" #include "winsock2.h" #pragma comment(lib, "wsock32.lib") struct sMailInfo //邮件信息 { char *mailbox_user_name; //用户登录邮箱的名称 char *mailbox_user_pwd; //用户登录邮箱的密码 char *mailbox_sender_show_name; //用户发送时显示的名称 char *mailbox_sender; //发送者的邮箱地址 char *mailbox_receiver; //接收者的邮箱地址 char *mail_title; //邮箱标题 char *mail_body; //邮件文本正文 char mail_server_ip_addr[32]; //服务器的IP char *mail_server_name; //服务器的名称(IP与名称二选一,优先取名称) sMailInfo() { memset(this, 0, sizeof(sMailInfo)); } }; class CSendMail { public: CSendMail(void); ~CSendMail(void); public: bool BeginSendMail(sMailInfo &smail_info); //发送邮件,需要在发送的时候初始化邮件信息 void AddFilePath(char *file_path); //添加附件的决定路径到附件列表中 void DeleteFilePath(char *file_path); //删除附件路径,如果有的话 void DeleteAllPath(void); //删除全部附件的路径 protected: void GetFileName(char *file_name, char *file_path); //从附件的路径中获取文件名称 void Char2Base64(char *buff_64, char *src_buff, int length);//把char类型转换成Base64类型 bool CReateSocket(SOCKET &sock); //建立socket连接 bool Logon(SOCKET &sock); //登录邮箱,主要进行发邮件前的准备工作 int GetFileData(char *file_path); //由文件路径获取附件内容 bool SendHead(SOCKET &sock); //发送邮件头 bool SendTextBody(SOCKET &sock); //发送邮件文本正文 bool SendFileBody(SOCKET &sock); //发送邮件附件 bool SendEnd(SOCKET &sock); //发送邮件结尾 protected: vector<char *> file_path_list_; //记录附件路径 char send_buff_[4096]; //发送缓冲区 char receive_buff_[1024]; char *file_buffer_; // 指向附件的内容 sMailInfo mail_info_; }; #endif
SendMail.cpp:
/************************************************************************ * 发送邮件模块 *可以发送文本和附件(支持多个附件一起发送) *Date:2011-12-01 ************************************************************************/ #include "StdAfx.h" #include "fstream" using namespace std; #include "SendMail.h" CSendMail::CSendMail(void) { file_buffer_ = NULL; memset(send_buff_, 0, sizeof(send_buff_)); memset(receive_buff_, 0, sizeof(receive_buff_)); } CSendMail::~CSendMail(void) { DeleteAllPath(); } void CSendMail::Char2Base64(char* buff_64, char* src_buff, int length) { //1 1 1 1 1 1 1 1 // 分配给pBuff64 ↑ 分配给pBuff64+1 // point所在的位置 static char base_64_encode[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";//base64所映射的字符表 int point; // 每一个源字符拆分的位置,可取2,4,6;初始为2 point = 2; int base_index; // base64字符的索引 char n = 0; // 上一个源字符的残留值 for(int index = 0; index < length; index++) { if(point == 2) { base_index = ((*src_buff) >> point) & 0x3f; // 取得pSrcBuff的高point位 } else if (point == 4) { base_index = ((*src_buff) >> point) & 0xf; // 取得pSrcBuff的高point位 } else if(point == 6) { base_index = ((*src_buff) >> point) & 0x3; // 取得pSrcBuff的高point位 } base_index += n; // 与pSrcBuff-1的低point结合组成Base64的索引 *buff_64++ = base_64_encode[base_index]; // 由索引表得到pBuff64 n = ((*src_buff) << (6 - point)); // 计算源字符中的残留值 n = n & 0x3f; //确保n的最高两位为0 point += 2; //源字符的拆分位置上升2 if(point == 8) //如果拆分位置为8说明pSrcBuff有6位残留,可以组成一个完整的Base64字符,所以直接再组合一次 { base_index = (*src_buff) & 0x3f; //提取低6位,这就是索引了 *buff_64++ =base_64_encode[base_index]; n = 0; // 残留值为0 point = 2; // 拆分位置设为2 } src_buff++; } if(n != 0) { *buff_64++ = base_64_encode[n]; } if(length % 3 == 2) // 如果源字符串长度不是3的倍数要用'='补全 { *buff_64 = '='; } else if(length % 3 == 1) { *buff_64++ = '='; *buff_64 = '='; } } void CSendMail::AddFilePath(char * file_path) // 添加附件路径 { if(file_path == NULL) { return; } int i; char *temp; for (int index = 0; index < file_path_list_.size(); index++) { temp = file_path_list_[index]; if(strcmp(file_path,temp) == 0) // 如果已经存在就不用再添加了 { return; } } file_path_list_.push_back(file_path); } void CSendMail::DeleteFilePath(char* file_path) // 删除附件路径 { int i; char* temp; for (int index = 0; index < file_path_list_.size(); index++) { temp = file_path_list_[index]; if (strcmp(file_path, temp) == 0) { file_path_list_.erase(file_path_list_.begin() + index); } } } void CSendMail::DeleteAllPath(void) { vector<char *>().swap(file_path_list_); // 清除容器并最小化它的容量 } int CSendMail::GetFileData(char* file_path) { file_buffer_ = NULL; if(file_path == NULL) { return 0; } // 通过读取文件的大小,来开辟数组空间的大小 ifstream fin(file_path); if (!fin.is_open()) { return -1; } fin.seekg(0, ios::end); int file_length = fin.tellg(); fin.seekg(0, ios::beg); file_buffer_ = new char[file_length]; fin.read(file_buffer_, file_length); fin.close(); return file_length; } void CSendMail::GetFileName(char* file_name,char* file_path) { if(file_path == NULL || file_name == NULL) { return; } for(int index = 0; index < (int)strlen(file_path); index++) { if(file_path[strlen(file_path) - 1 - index] == '\\') { memcpy(file_name, &file_path[strlen(file_path) - index], index); return; } } } bool CSendMail::CReateSocket(SOCKET &sock) { WORD wVersionRequested; WSADATA wsaData; int err; wVersionRequested = MAKEWORD( 2, 2 ); err = WSAStartup(wVersionRequested, &wsaData); if (err != 0) { return false; } if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2 ) { WSACleanup(); return false; } sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); if (sock == INVALID_SOCKET) { return false; } sockaddr_in server_addr; memset(&server_addr, 0, sizeof(sockaddr_in)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(25); // 发邮件一般都是25端口,SSL的是465端口 if(mail_info_.mail_server_name == "") { server_addr.sin_addr.s_addr = inet_addr(mail_info_.mail_server_ip_addr); // 直接使用IP地址 } else { struct hostent *hp = gethostbyname(mail_info_.mail_server_name); // 使用名称 server_addr.sin_addr.s_addr = *(int*)(*hp->h_addr_list); char* ip = inet_ntoa(server_addr.sin_addr); strcpy(mail_info_.mail_server_ip_addr, ip); } int ret = connect(sock, (sockaddr*)&server_addr, sizeof(server_addr)); // 建立连接 if (ret == SOCKET_ERROR) { return false; } return true; } bool CSendMail::Logon(SOCKET &sock) { recv(sock, receive_buff_, 1024, 0); memset(send_buff_, 0, sizeof(send_buff_)); sprintf_s(send_buff_, "HELO %s\r\n", mail_info_.mail_server_ip_addr); send(sock,send_buff_, strlen(send_buff_), 0); // 开始会话 memset(receive_buff_, 0, sizeof(receive_buff_)); recv(sock,receive_buff_, 1024, 0); if(receive_buff_[0] != '2' || receive_buff_[1] != '5' || receive_buff_[2] != '0') { return false; } memset(send_buff_, 0, sizeof(send_buff_)); sprintf_s(send_buff_, "AUTH LOGIN\r\n"); send(sock,send_buff_, strlen(send_buff_),0); // 请求登录 recv(sock,receive_buff_, 1024, 0); if(receive_buff_[0] != '3' || receive_buff_[1] != '3' || receive_buff_[2] != '4') { return false; } memset(send_buff_, 0, sizeof(send_buff_)); Char2Base64(send_buff_, mail_info_.mailbox_user_name, strlen(mail_info_.mailbox_user_name)); send_buff_[strlen(send_buff_)] = '\r'; send_buff_[strlen(send_buff_)] = '\n'; send(sock,send_buff_, strlen(send_buff_), 0); // 发送用户名 recv(sock,receive_buff_, 1024, 0); if(receive_buff_[0] != '3' || receive_buff_[1] != '3' || receive_buff_[2] != '4') { return false; } memset(send_buff_, 0, sizeof(send_buff_)); Char2Base64(send_buff_, mail_info_.mailbox_user_pwd, strlen(mail_info_.mailbox_user_pwd)); send_buff_[strlen(send_buff_)] = '\r'; send_buff_[strlen(send_buff_)] = '\n'; send(sock, send_buff_, strlen(send_buff_), 0); // 发送用户密码 recv(sock, receive_buff_, 1024, 0); if(receive_buff_[0] != '2' || receive_buff_[1] != '3' || receive_buff_[2] != '5') { return false; } return true; // 登录成功 } bool CSendMail::SendHead(SOCKET &sock) { int rt; memset(send_buff_, 0, sizeof(send_buff_)); sprintf_s(send_buff_, "MAIL FROM:<%s>\r\n", mail_info_.mailbox_sender); rt = send(sock,send_buff_, strlen(send_buff_), 0); if(rt != strlen(send_buff_)) { return false; } recv(sock, receive_buff_, 1024, 0); memset(send_buff_, 0, sizeof(send_buff_)); sprintf_s(send_buff_, "RCPT TO:<%s>\r\n", mail_info_.mailbox_receiver); rt = send(sock,send_buff_, strlen(send_buff_),0); if(rt != strlen(send_buff_)) { return false; } recv(sock, receive_buff_, 1024, 0); memset(send_buff_, 0, sizeof(send_buff_)); memcpy(send_buff_, "DATA\r\n", strlen("DATA\r\n")); rt = send(sock, send_buff_, strlen(send_buff_),0); if(rt != strlen(send_buff_)) { return false; } recv(sock, receive_buff_, 1024, 0); memset(send_buff_, 0, sizeof(send_buff_)); sprintf_s(send_buff_, "From:\"%s\"<%s>\r\n", mail_info_.mailbox_sender_show_name, mail_info_.mailbox_sender); sprintf_s(&send_buff_[strlen(send_buff_)], 150, "To:\"INVT.COM.CN\"<%s>\r\n", mail_info_.mailbox_receiver); sprintf_s(&send_buff_[strlen(send_buff_)], 150, "Subject:%s\r\nMime-Version: 1.0\r\nContent-Type: multipart/mixed; boundary=\"INVT\"\r\n\r\n", mail_info_.mail_title); rt = send(sock, send_buff_, strlen(send_buff_), 0); if(rt != strlen(send_buff_)) { return false; } return true; } bool CSendMail::SendTextBody(SOCKET &sock) { int rt; memset(send_buff_, 0, sizeof(send_buff_)); sprintf_s(send_buff_, "--INVT\r\nContent-Type: text/plain;\r\n charset=\"gb2312\"\r\n\r\n%s\r\n\r\n", mail_info_.mail_body); rt = send(sock, send_buff_, strlen(send_buff_), 0); if(rt != strlen(send_buff_)) { return false; } else { return true; } } bool CSendMail::SendFileBody(SOCKET &sock) { char* file_path; int rt; int len; char file_name[128]; int pt = 0; int dest_length = 0; for(int index = 0; index < file_path_list_.size(); index++) { pt = 0; memset(file_name, 0, 128); file_path = file_path_list_[index]; len = GetFileData(file_path); GetFileName(file_name,file_path); sprintf_s(send_buff_, "--INVT\r\nContent-Type: application/octet-stream;\r\n name=\"%s\"\r\nContent-Transfer-Encoding: base64\r\nContent-Disposition: attachment;\r\n filename=\"%s\"\r\n\r\n", file_name, file_name); send(sock,send_buff_, strlen(send_buff_), 0); while (pt < len) { memset(send_buff_,0,sizeof(send_buff_)); // 760为1024的最大整数,(760/4)*3=570 dest_length = 4096; Base64Encode((BYTE*)file_buffer_ + pt, min(len-pt,570), send_buff_, &dest_length); int len1 = strlen(send_buff_); pt += min(len-pt, 570); if(pt == len) { send_buff_[len1 ] = '\r'; send_buff_[len1+1] = '\n'; send_buff_[len1+2] = '\0'; } else send_buff_[len1 ] = '\0'; rt = send(sock,send_buff_, strlen(send_buff_), 0); if(rt != strlen(send_buff_)) { return false; } } if(len!=0) { delete[] file_buffer_; } } return true; } bool CSendMail::SendEnd(SOCKET &sock) { sprintf_s(send_buff_, "--INVT--\r\n.\r\n"); send(sock,send_buff_, strlen(send_buff_), 0); sprintf_s(send_buff_, "QUIT\r\n"); send(sock,send_buff_, strlen(send_buff_), 0); closesocket(sock); WSACleanup(); return true; } bool CSendMail::BeginSendMail(sMailInfo &smail_info) { memcpy(&mail_info_, &smail_info, sizeof(smail_info)); if(mail_info_.mail_body == NULL || mail_info_.mail_server_ip_addr == NULL || mail_info_.mail_server_name == NULL || mail_info_.mailbox_receiver == NULL || mail_info_.mailbox_sender == NULL || mail_info_.mailbox_sender_show_name == NULL || mail_info_.mail_title == NULL || mail_info_.mailbox_user_name == NULL || mail_info_.mailbox_user_pwd == NULL) { return false; } SOCKET sock; if(!CReateSocket(sock)) // 建立连接 { return false; } if(!Logon(sock)) // 登录邮箱 { return false; } if(!SendHead(sock)) // 发送邮件头 { return false; } if(!SendTextBody(sock)) // 发送邮件文本部分 { return false; } if(!SendFileBody(sock)) // 发送附件 { return false; } if(!SendEnd(sock)) // 结束邮件,并关闭sock { return false; } return true; }
2、测试使用以及效果
申请了一个测试邮箱(随意申请的)
账号是:[email protected]
密码是:123456789z
那么给自己发送邮箱(含有附件的测试工程如下)
// ConsoleApplication1.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include "iostream" using namespace std; #include "SendMail.h" void TestSendMail() { for (int index = 0; index < 10; index++) { CSendMail mail; sMailInfo info; // memcpy(info.m_pcIPAddr, "123.125.50.138", strlen("123.125.50.138")); info.mail_server_name = "smtp.163.com"; info.mailbox_receiver = "[email protected]"; info.mailbox_sender = "[email protected]"; info.mailbox_user_name = "qq2590995733"; info.mailbox_user_pwd = "123456789z"; info.mailbox_sender_show_name = "taobao客服"; info.mail_title = "游戏激活key"; info.mail_body = "XX,你妈妈喊你回家吃饭了!!!"; mail.AddFilePath("c:\\Download History.log"); mail.AddFilePath("c:\\1.jpg"); mail.AddFilePath("c:\\3.log"); mail.BeginSendMail(info); } } int _tmain(int argc, _TCHAR* argv[]) { TestSendMail(); return 0; }
效果如下:
3、补充
代码是参考csdn一个工程的,原来是依赖于mfc的,给修正了一下,不过还是需要加入atl(做base64的编码)。还得多多感谢原作者!
其实这里还是存在有问题的,比如发送出去的附件,在下载的时候,是有那么一点点被改变了。
不过这里主要是想要表达如何发送邮件,所以。。应该会在后面进行修复吧。
快速下载测试工程csdn(vs2012):
http://download.csdn.net/detail/zengraoli/7590761
c++使用stmp协议发送邮件(163的邮箱,TTL非SSL),布布扣,bubuko.com
时间: 2024-10-24 10:36:42