近段时间,实验室电脑的IP频繁地改变,搞得想用远程偷下懒都不行。这时想到的解决方法有:静态IP,动态域名,自己解决。静态IP虽然可以自己指定,但一关机后,与对方冲突就完了,作罢。免费的动态域名又要手机认证,也作罢。最后只能自己解决。解决方案是写一个程序不断地检测本机IP,如果改变了,就发邮件通知。检测本机IP很简单,就略过。这里介绍下怎样发邮件吧。
发邮件前,需要理解SMTP(Simple Mail Transfer Protocol)。SMTP是电子邮件从客户机传输到服务器或从某一个服务器传输到另一个服务器使用的传输协议。SMTP 是请求/响应协议,命令和响应都是基于 ASCII 文本,并以 CR 和 LF 符结束。响应包括一个表示返回状态的三位数字代码。在 TCP 协议 25 端口监听连接请求。其命令如下:
SMTP命令 | 命令说明 |
HELO <domain><CRLF> | 识别发送方到接收SMTP的一个HELO命令 |
AUTH LOGIN<CRLF> | 登陆服务器的命令。在这条命令之后,要发送用Base64编码后的用户名与密码进行登陆 |
MAIL FROM:<reverse-path><CRLF> | <reverse-path>为发送者地址。此命令告诉接收方一个新邮件发送的开始,并对所有的状态和缓冲区进行初始化。此命令开始一个邮件传输处理,最终完成将邮件数据传送到一个或多个邮箱中。 |
RCPT TO:<forward-path><CRLF> | <forward-path>标识各个邮件接收者的地址 |
DATA <CRLF> | 接收SMTP将把其后的行为看作邮件数据去处理,以<CRLF>.<CRLF>标识数据的结尾。 |
REST <CRLF> | 退出/复位当前的邮件传输 |
NOOP <CRLF> | 要求接收SMTP仅做OK应答。(用于测试) |
QUIT <CRLF> | 要求接收SMTP返回一个OK应答并关闭传输。 |
VRFY <string> <CRLF> | 验证指定的邮箱是否存在,由于安全因素,服务器多禁止此命令。 |
EXPN <string> <CRLF> | 验证给定的邮箱列表是否存在,扩充邮箱列表,也常禁止使用。 |
HELP <CRLF> | 查询服务器支持什么命令 |
了解了这些命令后, 就可以发邮件了。发一封简单邮件的交互图如下,其中邮箱使用163邮箱:
C++发邮件的实现表示如下:
//author: Zero //facade of function send() void Send(SOCKET& s, string& data) { if( send(s, data.c_str(), data.length(), 0) == SOCKET_ERROR ) { cerr<<"send data \""<<data<<"\" error"<<endl; } } //facade of function recv() void Recv(SOCKET& s, char* buf, int len) { memset(buf, 0, len); if( recv(s, buf, len, 0) == SOCKET_ERROR ) { cerr<<"error, while receiving data"<<endl; } } string Base64Encode(const string& src) { int i, j, srcLen = src.length(); string dst(srcLen / 3 * 4 + 4, 0); for(i = 0, j= 0; i <=srcLen - 3; i+=3, j+=4) { dst[j] = (src[i] & 0xFC) >> 2; dst[j+1] = ((src[i] & 0x03) << 4) + ((src[i+1] & 0xF0) >> 4); dst[j+2] = ((src[i+1] & 0x0F) << 2) + ((src[i+2] & 0xC0) >> 6); dst[j+3] = src[i+2] & 0x3F; } if( srcLen % 3 == 1 ) { dst[j] = (src[i] & 0xFC) >> 2; dst[j+1] = ((src[i] & 0x03) << 4); dst[j+2] = 64; dst[j+3] = 64; j += 4; } else if( srcLen % 3 == 2 ) { dst[j] = (src[i] & 0xFC) >> 2; dst[j+1] = ((src[i] & 0x03) << 4) + ((src[i+1] & 0xF0) >> 4); dst[j+2] = ((src[i+1] & 0x0F) << 2); dst[j+3] = 64; j+=4; } static unsigned char *base64 = (unsigned char*)("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="); for(i = 0; i < j; ++i) { //map 6 bit value to base64 ASCII character dst[i] = base64[(int)dst[i]]; } return dst; } bool SendEmail(const string& smtpServer, const string& username, const string& pw, const string& to, const string& data) { hostent *ph = gethostbyname(smtpServer.c_str()); if( ph == NULL ) { cerr<<"no host: "<<smtpServer<<endl; return false; } sockaddr_in sin; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(25); //port of SMTP memcpy(&sin.sin_addr.S_un.S_addr, ph->h_addr_list[0], ph->h_length); //connect to the mail server SOCKET s = socket(PF_INET, SOCK_STREAM, 0); if( connect(s, (sockaddr*)&sin, sizeof(sin)) ) { cerr<<"failed to connect the mail server"<<endl; return false; } // char recvBuffer[1024]; Recv(s, recvBuffer, sizeof(recvBuffer)); //wait for greeting message Send(s, "HELO " + smtpServer + "\r\n"); Recv(s, recvBuffer, sizeof(recvBuffer)); //should recv "250 OK" //start to log in Send(s, (string)"auth login\r\n"); Recv(s, recvBuffer, sizeof(recvBuffer)); //should recv "334 username:"(This is the decode message) Send(s, Base64Encode(username) + "\r\n"); Recv(s, recvBuffer, sizeof(recvBuffer)); if( string(recvBuffer).substr(0, 3) != "334" ) { cout<<"username is error!"<<endl; return false; } Send(s, Base64Encode(pw) + "\r\n"); Recv(s, recvBuffer, sizeof(recvBuffer)); if( string(recvBuffer).substr(0, 3) != "235") { cout<<"password error"<<endl; return false; } //Set sender Send(s, "mail from:<" + username + ">\r\n"); Recv(s, recvBuffer, sizeof(recvBuffer)); //should recv "250 Mail OK" //set receiver Send(s, "rcpt to:<" + to + ">\r\n"); Recv(s, recvBuffer, sizeof(recvBuffer)); //should recv "250 Mail OK" //send data Send(s, (string)"data\r\n"); Recv(s, recvBuffer, sizeof(recvBuffer)); //should recv "354 End data with <CR><LF>.<CR><LF>" Send(s, "to:" + to + "\r\n" + "subject:the newest IP\r\n\r\n" + data + "\r\n.\r\n"); Recv(s, recvBuffer, sizeof(recvBuffer)); Send(s, (string)"quit\r\n"); Recv(s, recvBuffer, sizeof(recvBuffer)); closesocket(s); return true; }