C++实现邮箱发送
本电子邮件客户端实现本质上在于向邮箱服务器建立链接,从而进行一系列的身份校验,输入收件人邮箱以及要发送的信息等等。
首先进行winsock的版本规定,建立套接字,并将本机的名称信息等与服务器端进行绑定,并选择一个端口进行数据的发送与接收。
主流的邮箱主要有QQ邮箱,163邮箱。不同的邮箱有不同的host,QQ对应于smtp.qq.com ,163 邮箱对应于smtp.163.com。本程序设计两种邮箱均可进行发送。当要进行邮件发送的时候需要向服务器发送请求,在进行身份验证的时候需要使用用户邮箱的授权码以及用户名,并通过base64加密进行身份校验,以保证数据的安全性,并对数据进行压缩。
需要注意的是,在发送邮件时,客户端对于传送的数据有着很严格的要求,主题的开头,标识等等,即使多一个空格也是不行的,这也是QQ邮箱发送使用ui界面的一个比较重要的原因。
-
使用命令行发送邮箱
首先需要开启 telnet 客户端功能
选中 Telnet Client 服务选择开启。
命令行输入:
telnet smtp.163.com 25(163邮箱服务)
或者 telnet smtp.qq.com 25(QQ 邮箱服务),效果大致如下
(注: 25 为 smtp 的默认服务端口)
与服务器打招呼(hello 是任意的一句话,最好不要出现中文),之后会有下面输出
ehlo hello
开始认证
auth login
输入base64加密的用户名和授权码(授权码获取这里不赘述,在邮箱主页里获取),base64加密网站推荐—> 传送门
输入以下格式输入发件人,收件人的信息
输入 DATA,出现以上354回复
输入subject:标题,中间再空一行,接着输入正文,最后输入. 表示输入结束。
出现 250 的信息说明发送成功
大致输入如下所示
telnet smtp.qq.com 25
ehlo hello
auth login
base64(用户名)
base64(授权码)
mail from:ruerlen@qq.com
rcpt to:2927357751@qq.com
data
subject:nihao
你好
.
-
使用 c++ socket 连接服务器进行发送,要点便是通过与smtp服务器建立联系,模拟命令行与服务器进行交互
base64 加密函数:
// base64 加密函数 string base64(string str) //base64加密算法 { string base64_table="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; int str_len = str.length(); string res=""; for (int strp=0; strp<str_len/3*3; strp+=3) { res+=base64_table[str[strp]>>2]; res+=base64_table[(str[strp]&0x3)<<4 | (str[strp+1])>>4]; res+=base64_table[(str[strp+1]&0xf)<<2 | (str[strp+2])>>6]; res+=base64_table[(str[strp+2])&0x3f]; //cout<<res<<endl; } if (str_len%3==1) { int pos=str_len/3 * 3; res += base64_table[str[pos]>>2]; res += base64_table[(str[pos]&0x3)<<4]; res += "="; res += "="; } else if (str_len%3==2) { int pos=str_len/3 * 3; res += base64_table[str[pos]>>2]; res += base64_table[(str[pos]&0x3)<<4 | (str[pos+1])>>4]; res += base64_table[(str[pos+1]&0xf)<<2]; res += "="; } return res; }
设置winsock版本,初始化套接字
WSADATA wsaData; WORD wVersionRequested = MAKEWORD(2, 1); //版本2.1 int err = WSAStartup(wVersionRequested, &wsaData); // cout << "WSAStartup:" << err << endl; SOCKET sockClient; sockClient = socket(AF_INET, SOCK_STREAM, 0);
得到smtp服务器的网络字节序的ip地址并向服务器发送请求
SOCKADDR_IN addrServer; //服务端地址 addrServer.sin_addr.S_un.S_addr = *((DWORD *)pHostent->h_addr_list[0]); //得到smtp服务器的网络字节序的ip地址 addrServer.sin_family = AF_INET; addrServer.sin_port = htons(25); //连接端口25 err = connect(sockClient, (SOCKADDR*)&addrServer, sizeof(SOCKADDR)); //向服务器发送请求
完整代码:
#include <iostream> #include <string> #include <WinSock2.h> //适用平台 Windows using namespace std; #pragma comment(lib, "ws2_32.lib") /*链接ws2_32.lib动态链接库*/ using namespace std; // base64 加密函数 string base64(string str) //base64加密算法 { string base64_table="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; int str_len = str.length(); string res=""; for (int strp=0; strp<str_len/3*3; strp+=3) { res+=base64_table[str[strp]>>2]; res+=base64_table[(str[strp]&0x3)<<4 | (str[strp+1])>>4]; res+=base64_table[(str[strp+1]&0xf)<<2 | (str[strp+2])>>6]; res+=base64_table[(str[strp+2])&0x3f]; //cout<<res<<endl; } if (str_len%3==1) { int pos=str_len/3 * 3; res += base64_table[str[pos]>>2]; res += base64_table[(str[pos]&0x3)<<4]; res += "="; res += "="; } else if (str_len%3==2) { int pos=str_len/3 * 3; res += base64_table[str[pos]>>2]; res += base64_table[(str[pos]&0x3)<<4 | (str[pos+1])>>4]; res += base64_table[(str[pos+1]&0xf)<<2]; res += "="; } return res; } int main() { char buff[500]; //recv函数返回的结果 string message; string info; string subject; // telnet smtp.qq.com WSADATA wsaData; WORD wVersionRequested = MAKEWORD(2, 1); //版本2.1 int err = WSAStartup(wVersionRequested, &wsaData); // cout << "WSAStartup:" << err << endl; SOCKET sockClient; sockClient = socket(AF_INET, SOCK_STREAM, 0); HOSTENT* pHostent;//主机的名称,地址信息等 while(1) { int flag; cout<<"如果您是QQ邮箱请按1,163邮箱请按2"<<endl; cin>>flag; if(flag == 1) { pHostent = gethostbyname("smtp.qq.com"); //得到有关于域名的信息,链接到qq邮箱服务器 } else { pHostent = gethostbyname("smtp.163.com"); //得到有关于域名的信息,链接到qq邮箱服务器 } SOCKADDR_IN addrServer; //服务端地址 addrServer.sin_addr.S_un.S_addr = *((DWORD *)pHostent->h_addr_list[0]); //得到smtp服务器的网络字节序的ip地址 addrServer.sin_family = AF_INET; addrServer.sin_port = htons(25); //连接端口25 err = connect(sockClient, (SOCKADDR*)&addrServer, sizeof(SOCKADDR)); //向服务器发送请求 //cout << "connect:" << err << endl; buff[recv(sockClient, buff, 500, 0)] = '\0'; // cout << "connect:" << buff << endl; // message = "ehlo qq.com\r\n"; message = "ehlo hello\r\n"; // 先打个招呼 send(sockClient, message.c_str(), message.length(), 0); //发送ehlo命令 buff[recv(sockClient, buff, 500, 0)] = '\0'; //接收返回值 // cout << "helo:" << buff << endl; //输出返回值 message = "auth login\r\n"; send(sockClient, message.c_str(), message.length(), 0); buff[recv(sockClient, buff, 500, 0)] = '\0'; //cout << "auth login:" << buff << endl; /* 发送base64加密的用户名、密码 */ string Smail; cout<< "输入您的邮箱:"; cin>>Smail; string name = Smail.substr(0,Smail.find("@")); // cout<<name<<endl; message = base64(name) + "\r\n"; //base64 编码的用户名 send(sockClient, message.c_str(), message.length(), 0); buff[recv(sockClient, buff, 500, 0)] = '\0'; //cout << "usrname:" << buff << endl; string Swd; cout << "请输入您的授权码:"; cin>>Swd; message = base64(Swd) + "\r\n";//base64 编码的密码 send(sockClient, message.c_str(), message.length(), 0); buff[recv(sockClient, buff, 500, 0)] = '\0'; cout << "password:" << buff << endl; string Rmail; cout << "输入收件人邮箱:"; cin >> Rmail; message = "mail from:<"+Smail+">\r\n"; // message = "MAIL FROM:<XXX@qq.com> \r\nRCPT TO:<XXX@163.com> \r\n"; send(sockClient, message.c_str(), message.length(), 0); buff[recv(sockClient, buff, 500, 0)] = '\0'; message = "rcpt to:<"; message.append(Rmail); message.append(">\r\n"); //cout << "message=" << message; send(sockClient, message.c_str(), message.length(), 0); buff[recv(sockClient, buff, 500, 0)] = '\0'; message = "DATA\r\n"; send(sockClient, message.c_str(), message.length(), 0); buff[recv(sockClient, buff, 500, 0)] = '\0'; message = "From: "+Smail+"\r\nTo: "+Rmail+"\r\nsubject:"; cout<<"主题:"; cin>>subject; message.append(subject); message.append("\r\n\r\n"); cout<<"内容:"; cin>>info; message.append(info); message.append("\r\n.\r\n"); send(sockClient, message.c_str(), message.length(), 0); buff[recv(sockClient, buff, 500, 0)] = '\0'; cout<<" 发送成功!"<<endl; cout<<buff; message = "QUIT\r\n"; send(sockClient, message.c_str(), message.length(), 0); buff[recv(sockClient, buff, 500, 0)] = '\0'; char mes; cout<<"输入Q退出,R再来一封邮件"<<endl; cin>>mes; if(mes == 'Q') { break; } } // cout << "QUIT:" << buff << endl; cout << "退出成功!"<<endl; system("pause"); }
运行效果如下:
对方可以收到邮件