C++实现发送邮件
备注:代码是从别人博客拷贝下来,如有问题;请联系 QQ:972510639
SmtpEmail.h
1 #ifndef __SMTP_EMAIL_H__ //避免重复包含 2 #define __SMTP_EMAIL_H__ 3 4 #include <iostream> 5 #include <list> 6 #include <WinSock2.h> 7 using namespace std; 8 9 const int MAXLEN = 1024; 10 const int MAX_FILE_LEN = 6000; 11 12 static const char base64Char[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 13 14 struct FILEINFO /*用来记录文件的一些信息*/ 15 { 16 char fileName[128]; /*文件名称*/ 17 char filePath[256]; /*文件绝对路径*/ 18 }; 19 20 class CSmtpEmail 21 { 22 public: 23 CSmtpEmail(void); 24 CSmtpEmail( 25 int port, 26 string srvDomain, //smtp服务器域名 27 string userName, //用户名 28 string password, //密码 29 string targetEmail, //目的邮件地址 30 string emailTitle, //主题 31 string content //内容 32 ); 33 public: 34 ~CSmtpEmail(void); 35 public: 36 int port; 37 public: 38 string domain; 39 string user; 40 string pass; 41 string targetAddr; 42 string title; 43 string content; 44 /*为了方便添加文件,删除文件神马的,使用list容器最为方便,相信大家在数据结构里面都学过*/ 45 list <FILEINFO *> listFile; 46 47 public: 48 char buff[MAXLEN + 1]; 49 int buffLen; 50 SOCKET sockClient; //客户端的套接字 51 public: 52 bool CreateConn(); /*创建连接*/ 53 54 bool Send(string &message); 55 bool Recv(); 56 57 void FormatEmailHead(string &email);//格式化要发送的邮件头部 58 int Login(); 59 bool SendEmailHead(); //发送邮件头部信息 60 bool SendTextBody(); //发送文本信息 61 //bool SendAttachment(); //发送附件 62 int SendAttachment_Ex(); 63 bool SendEnd(); 64 public: 65 void AddAttachment(string &filePath); //添加附件 66 void DeleteAttachment(string &filePath); //删除附件 67 void DeleteAllAttachment(); //删除所有的附件 68 69 void SetSrvDomain(string &domain); 70 void SetUserName(string &user); 71 void SetPass(string &pass); 72 void SetTargetEmail(string &targetAddr); 73 void SetEmailTitle(string &title); 74 void SetContent(string &content); 75 void SetPort(int port); 76 int SendEmail_Ex(); 77 /*关于错误码的说明:1.网络错误导致的错误2.用户名错误3.密码错误4.文件不存在0.成功*/ 78 char* base64Encode(char const* origSigned, unsigned origLength); 79 }; 80 81 #endif // !__SMTP_EMAIL_H__
SmtpEmail.cpp
1 #include "stdafx.h" 2 #include "SmtpEmail.h" 3 #include <iostream> 4 #include <fstream> 5 using namespace std; 6 7 #pragma comment(lib, "ws2_32.lib") /*链接ws2_32.lib动态链接库*/ 8 9 /*base64采用别人的编码,不过,这不是重点,重点是我完成了我的一个比较好的邮件发送客户端*/ 10 char* CSmtpEmail::base64Encode(char const* origSigned, unsigned origLength) 11 { 12 unsigned char const* orig = (unsigned char const*)origSigned; // in case any input bytes have the MSB set 13 if (orig == NULL) return NULL; 14 15 unsigned const numOrig24BitValues = origLength / 3; 16 bool havePadding = origLength > numOrig24BitValues * 3; 17 bool havePadding2 = origLength == numOrig24BitValues * 3 + 2; 18 unsigned const numResultBytes = 4 * (numOrig24BitValues + havePadding); 19 char* result = new char[numResultBytes + 3]; // allow for trailing '/0' 20 21 // Map each full group of 3 input bytes into 4 output base-64 characters: 22 unsigned i; 23 for (i = 0; i < numOrig24BitValues; ++i) 24 { 25 result[4 * i + 0] = base64Char[(orig[3 * i] >> 2) & 0x3F]; 26 result[4 * i + 1] = base64Char[(((orig[3 * i] & 0x3) << 4) | (orig[3 * i + 1] >> 4)) & 0x3F]; 27 result[4 * i + 2] = base64Char[((orig[3 * i + 1] << 2) | (orig[3 * i + 2] >> 6)) & 0x3F]; 28 result[4 * i + 3] = base64Char[orig[3 * i + 2] & 0x3F]; 29 } 30 31 // Now, take padding into account. (Note: i == numOrig24BitValues) 32 if (havePadding) 33 { 34 result[4 * i + 0] = base64Char[(orig[3 * i] >> 2) & 0x3F]; 35 if (havePadding2) 36 { 37 result[4 * i + 1] = base64Char[(((orig[3 * i] & 0x3) << 4) | (orig[3 * i + 1] >> 4)) & 0x3F]; 38 result[4 * i + 2] = base64Char[(orig[3 * i + 1] << 2) & 0x3F]; 39 } 40 else 41 { 42 result[4 * i + 1] = base64Char[((orig[3 * i] & 0x3) << 4) & 0x3F]; 43 result[4 * i + 2] = '='; 44 } 45 result[4 * i + 3] = '='; 46 } 47 48 result[numResultBytes] = '\0'; 49 return result; 50 } 51 CSmtpEmail::CSmtpEmail(void) 52 { 53 this->content = ""; 54 this->port = 25; 55 this->user = ""; 56 this->pass = ""; 57 this->targetAddr = ""; 58 this->title = ""; 59 this->domain = ""; 60 61 WORD wVersionRequested; 62 WSADATA wsaData; 63 int err; 64 wVersionRequested = MAKEWORD(2, 1); 65 err = WSAStartup(wVersionRequested, &wsaData); 66 this->sockClient = 0; 67 68 } 69 70 CSmtpEmail::~CSmtpEmail(void) 71 { 72 DeleteAllAttachment(); 73 closesocket(sockClient); 74 WSACleanup(); 75 } 76 77 78 CSmtpEmail::CSmtpEmail( 79 int port, 80 string srvDomain, 81 string userName, 82 string password, 83 string targetEmail, 84 string emailTitle, 85 string content 86 ) 87 { 88 this->content = content; 89 this->port = port; 90 this->user = userName; 91 this->pass = password; 92 this->targetAddr = targetEmail; 93 this->title = emailTitle; 94 this->domain = srvDomain; 95 96 WORD wVersionRequested; 97 WSADATA wsaData; 98 int err; 99 wVersionRequested = MAKEWORD(2, 1); 100 err = WSAStartup(wVersionRequested, &wsaData); 101 this->sockClient = 0; 102 } 103 104 bool CSmtpEmail::CreateConn() 105 { 106 //为建立socket对象做准备,初始化环境 107 SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0); //建立socket对象 108 SOCKADDR_IN addrSrv; 109 HOSTENT* pHostent; 110 pHostent = gethostbyname(domain.c_str()); //得到有关于域名的信息 111 112 addrSrv.sin_addr.S_un.S_addr = *((DWORD *)pHostent->h_addr_list[0]); //得到smtp服务器的网络字节序的ip地址 113 addrSrv.sin_family = AF_INET; 114 addrSrv.sin_port = htons(port); 115 int err = connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)); //向服务器发送请求 116 if (err != 0) 117 { 118 return false; 119 //printf("链接失败\n"); 120 } 121 this->sockClient = sockClient; 122 if (false == Recv()) 123 { 124 return false; 125 } 126 return true; 127 } 128 129 bool CSmtpEmail::Send(string &message) 130 { 131 int err = send(sockClient, message.c_str(), message.length(), 0); 132 if (err == SOCKET_ERROR) 133 { 134 return false; 135 } 136 string message01; 137 cout << message.c_str() << endl; 138 return true; 139 } 140 141 bool CSmtpEmail::Recv() 142 { 143 memset(buff, 0, sizeof(char)* (MAXLEN + 1)); 144 int err = recv(sockClient, buff, MAXLEN, 0); //接收数据 145 if (err == SOCKET_ERROR) 146 { 147 return false; 148 } 149 buff[err] = '\0'; 150 cout << buff << endl; 151 return true; 152 } 153 154 int CSmtpEmail::Login() 155 { 156 string sendBuff; 157 sendBuff = "EHLO "; 158 sendBuff += user; 159 sendBuff += "\r\n"; 160 161 if (false == Send(sendBuff) || false == Recv()) //既接收也发送 162 { 163 return 1; /*1表示发送失败由于网络错误*/ 164 } 165 166 sendBuff.empty(); 167 sendBuff = "AUTH LOGIN\r\n"; 168 if (false == Send(sendBuff) || false == Recv()) //请求登陆 169 { 170 return 1; /*1表示发送失败由于网络错误*/ 171 } 172 173 sendBuff.empty(); 174 int pos = user.find('@', 0); 175 sendBuff = user.substr(0, pos); //得到用户名 176 177 char *ecode; 178 /*在这里顺带扯一句,关于string类的length函数与C语言中的strlen函数的区别,strlen计算出来的长度,只到'\0'字符为止,而string::length()函数实际上返回的是string类中字符数组的大小,你自己可以测试一下,这也是为什么我下面不使用string::length()的原因*/ 179 180 ecode = base64Encode(sendBuff.c_str(), strlen(sendBuff.c_str())); 181 sendBuff.empty(); 182 sendBuff = ecode; 183 sendBuff += "\r\n"; 184 delete[]ecode; 185 186 if (false == Send(sendBuff) || false == Recv()) //发送用户名,并接收服务器的返回 187 { 188 return 1; /*错误码1表示发送失败由于网络错误*/ 189 } 190 191 sendBuff.empty(); 192 ecode = base64Encode(pass.c_str(), strlen(pass.c_str())); 193 sendBuff = ecode; 194 sendBuff += "\r\n"; 195 delete[]ecode; 196 197 if (false == Send(sendBuff) || false == Recv()) //发送用户密码,并接收服务器的返回 198 { 199 return 1; /*错误码1表示发送失败由于网络错误*/ 200 } 201 202 if (NULL != strstr(buff, "550")) 203 { 204 return 2;/*错误码2表示用户名错误*/ 205 } 206 207 if (NULL != strstr(buff, "535")) /*535是认证失败的返回*/ 208 { 209 return 3; /*错误码3表示密码错误*/ 210 } 211 return 0; 212 } 213 214 bool CSmtpEmail::SendEmailHead() //发送邮件头部信息 215 { 216 string sendBuff; 217 sendBuff = "MAIL FROM: <" + user + ">\r\n"; 218 if (false == Send(sendBuff) || false == Recv()) 219 { 220 return false; /*表示发送失败由于网络错误*/ 221 } 222 223 224 sendBuff.empty(); 225 sendBuff = "RCPT TO: <" + targetAddr + ">\r\n"; 226 if (false == Send(sendBuff) || false == Recv()) 227 { 228 return false; /*表示发送失败由于网络错误*/ 229 } 230 231 sendBuff.empty(); 232 sendBuff = "DATA\r\n"; 233 if (false == Send(sendBuff) || false == Recv()) 234 { 235 return false; //表示发送失败由于网络错误 236 } 237 238 sendBuff.empty(); 239 FormatEmailHead(sendBuff); 240 if (false == Send(sendBuff)) 241 //发送完头部之后不必调用接收函数,因为你没有\r\n.\r\n结尾,服务器认为你没有发完数据,所以不会返回什么值 242 { 243 return false; /*表示发送失败由于网络错误*/ 244 } 245 return true; 246 } 247 248 void CSmtpEmail::FormatEmailHead(string &email) 249 {/*格式化要发送的内容*/ 250 email = "From: "; 251 email += user; 252 email += "\r\n"; 253 254 email += "To: "; 255 email += targetAddr; 256 email += "\r\n"; 257 258 email += "Subject: "; 259 email += title; 260 email += "\r\n"; 261 262 email += "MIME-Version: 1.0"; 263 email += "\r\n"; 264 265 email += "Content-Type: multipart/mixed;boundary=qwertyuiop"; 266 email += "\r\n"; 267 email += "\r\n"; 268 } 269 270 bool CSmtpEmail::SendTextBody() /*发送邮件文本*/ 271 { 272 string sendBuff; 273 sendBuff = "--qwertyuiop\r\n"; 274 sendBuff += "Content-Type: text/plain;"; 275 sendBuff += "charset=\"gb2312\"\r\n\r\n"; 276 sendBuff += content; 277 sendBuff += "\r\n\r\n"; 278 return Send(sendBuff); 279 } 280 281 int CSmtpEmail::SendAttachment_Ex() /*发送附件*/ 282 { 283 for (list<FILEINFO *>::iterator pIter = listFile.begin(); pIter != listFile.end(); pIter++) 284 { 285 cout << "Attachment is sending ~~~~~" << endl; 286 cout << "Please be patient!" << endl; 287 string sendBuff; 288 sendBuff = "--qwertyuiop\r\n"; 289 sendBuff += "Content-Type: application/octet-stream;\r\n"; 290 sendBuff += " name=\""; 291 sendBuff += (*pIter)->fileName; 292 sendBuff += "\""; 293 sendBuff += "\r\n"; 294 295 sendBuff += "Content-Transfer-Encoding: base64\r\n"; 296 sendBuff += "Content-Disposition: attachment;\r\n"; 297 sendBuff += " filename=\""; 298 sendBuff += (*pIter)->fileName; 299 sendBuff += "\""; 300 301 sendBuff += "\r\n"; 302 sendBuff += "\r\n"; 303 Send(sendBuff); 304 ifstream ifs((*pIter)->filePath, ios::in | ios::binary); 305 if (false == ifs.is_open()) 306 { 307 return 4; /*错误码4表示文件打开错误*/ 308 } 309 char fileBuff[MAX_FILE_LEN]; 310 char *chSendBuff; 311 memset(fileBuff, 0, sizeof(fileBuff)); 312 /*文件使用base64加密传送*/ 313 while (ifs.read(fileBuff, MAX_FILE_LEN)) 314 { 315 //cout << ifs.gcount() << endl; 316 chSendBuff = base64Encode(fileBuff, MAX_FILE_LEN); 317 chSendBuff[strlen(chSendBuff)] = '\r'; 318 chSendBuff[strlen(chSendBuff)] = '\n'; 319 send(sockClient, chSendBuff, strlen(chSendBuff), 0); 320 delete[]chSendBuff; 321 } 322 //cout << ifs.gcount() << endl; 323 chSendBuff = base64Encode(fileBuff, ifs.gcount()); 324 chSendBuff[strlen(chSendBuff)] = '\r'; 325 chSendBuff[strlen(chSendBuff)] = '\n'; 326 int err = send(sockClient, chSendBuff, strlen(chSendBuff), 0); 327 328 if (err != strlen(chSendBuff)) 329 { 330 cout << "文件传送出错!" << endl; 331 return 1; 332 } 333 delete[]chSendBuff; 334 } 335 return 0; 336 } 337 338 bool CSmtpEmail::SendEnd() /*发送结尾信息*/ 339 { 340 string sendBuff; 341 sendBuff = "--qwertyuiop--"; 342 sendBuff += "\r\n.\r\n"; 343 if (false == Send(sendBuff) || false == Recv()) 344 { 345 return false; 346 } 347 cout << buff << endl; 348 sendBuff.empty(); 349 sendBuff = "QUIT\r\n"; 350 return (Send(sendBuff) && Recv()); 351 } 352 353 int CSmtpEmail::SendEmail_Ex() 354 { 355 if (false == CreateConn()) 356 { 357 return 1; 358 } 359 //Recv(); 360 int err = Login(); //先登录 361 if (err != 0) 362 { 363 return err; //错误代码必须要返回 364 } 365 if (false == SendEmailHead()) //发送EMAIL头部信息 366 { 367 return 1; /*错误码1是由于网络的错误*/ 368 } 369 if (false == SendTextBody()) 370 { 371 return 1; /*错误码1是由于网络的错误*/ 372 } 373 err = SendAttachment_Ex(); 374 if (err != 0) 375 { 376 return err; 377 } 378 if (false == SendEnd()) 379 { 380 return 1; /*错误码1是由于网络的错误*/ 381 } 382 return 0; /*0表示没有出错*/ 383 } 384 385 void CSmtpEmail::AddAttachment(string &filePath) //添加附件 386 { 387 FILEINFO *pFile = new FILEINFO; 388 strcpy_s(pFile->filePath, filePath.c_str()); 389 const char *p = filePath.c_str(); 390 strcpy_s(pFile->fileName, p + filePath.find_last_of("\\") + 1); 391 listFile.push_back(pFile); 392 } 393 394 void CSmtpEmail::DeleteAttachment(string &filePath) //删除附件 395 { 396 list<FILEINFO *>::iterator pIter; 397 for (pIter = listFile.begin(); pIter != listFile.end(); pIter++) 398 { 399 if (strcmp((*pIter)->filePath, filePath.c_str()) == 0) 400 { 401 FILEINFO *p = *pIter; 402 listFile.remove(*pIter); 403 delete p; 404 break; 405 } 406 } 407 } 408 409 void CSmtpEmail::DeleteAllAttachment() /*删除所有的文件*/ 410 { 411 for (list<FILEINFO *>::iterator pIter = listFile.begin(); pIter != listFile.end();) 412 { 413 FILEINFO *p = *pIter; 414 pIter = listFile.erase(pIter); 415 delete p; 416 } 417 } 418 419 void CSmtpEmail::SetSrvDomain(string &domain) 420 { 421 this->domain = domain; 422 } 423 424 void CSmtpEmail::SetUserName(string &user) 425 { 426 this->user = user; 427 } 428 429 void CSmtpEmail::SetPass(string &pass) 430 { 431 this->pass = pass; 432 } 433 void CSmtpEmail::SetTargetEmail(string &targetAddr) 434 { 435 this->targetAddr = targetAddr; 436 } 437 void CSmtpEmail::SetEmailTitle(string &title) 438 { 439 this->title = title; 440 } 441 void CSmtpEmail::SetContent(string &content) 442 { 443 this->content = content; 444 } 445 void CSmtpEmail::SetPort(int port) 446 { 447 this->port = port; 448 }
实现
1 CSmtpEmail smtp( 2 25, /*smtp端口*/ 3 "smtp.qq.com", /*smtp服务器地址*/ 4 "972510639@qq.com", /*你的邮箱地址*/ 5 "******", /*邮箱密码*///QQ邮箱密码获取 详见https://service.mail.qq.com/cgi-bin/help?subtype=1&&id=28&&no=1001256 163只需要原来密码 6 "8473003889@qq.com", /*目的邮箱地址*/ 7 "邮件测试", /*主题*/ 8 "测试邮件无需回复!!" /*邮件正文*/ 9 ); 10 /** 11 //添加附件时注意,\一定要写成\\,因为转义字符的缘故 12 string filePath("D:\\课程设计报告.doc"); 13 smtp.AddAttachment(filePath); 14 */ 15 16 /*还可以调用CSmtp::DeleteAttachment函数删除附件,还有一些函数,自己看头文件吧!*/ 17 //filePath = "C:\\Users\\李懿虎\\Desktop\\sendEmail.cpp"; 18 //smtp.AddAttachment(filePath); 19 20 int err; 21 if ((err = smtp.SendEmail_Ex()) != 0) 22 { 23 if (err == 1) 24 AfxMessageBox(L"错误1: 由于网络不畅通,发送失败!"); 25 if (err == 2) 26 AfxMessageBox(L"错误2: 用户名错误,请核对!"); 27 if (err == 3) 28 AfxMessageBox(L"错误3: 用户密码错误,请核对!"); 29 if (err == 4) 30 AfxMessageBox(L"错误4: 请检查附件目录是否正确,以及文件是否存在!"); 31 }