近段時間,實驗室電腦的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; }