用C++发邮件

简介: 近段时间,实验室电脑的IP频繁地改变,搞得想用远程偷下懒都不行。这时想到的解决方法有:静态IP,动态域名,自己解决。静态IP虽然可以自己指定,但一关机后,与对方冲突就完了,作罢。免费的动态域名又要手机认证,也作罢。

近段时间,实验室电脑的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;
}
复制代码

 

 
目录
相关文章
|
1月前
|
C# 数据安全/隐私保护
C#使用 MailKit 收发邮件
【10月更文挑战第15天】在C#中使用MailKit库可轻松实现邮件的发送与接收。首先需通过NuGet包管理器安装MailKit,并编写代码设置发件人、收件人、邮件主题及正文,连接邮件服务器并认证后即可发送邮件。接收邮件时,同样需连接服务器并认证,然后打开收件箱遍历读取邮件信息。整个过程需根据实际邮件服务器配置相应参数。
|
数据安全/隐私保护
使用MailUtils发送邮件
使用MailUtils发送邮件
|
网络协议 安全
调用邮件服务器进行发邮件
业务部门需求,要求阿里云上的服务器通过TCP 25端口进行外部邮件的发送
818 0
调用邮件服务器进行发邮件
阿里云如何添加邮箱解析来实现收发邮件服务
当你买域名后,要在阿里云添加邮箱解析,邮箱解析是把域名指向邮箱的服务器地址,实现邮箱的收发邮件服务。一、联系邮箱提供商,获取邮箱解析记录,这部分数据一般在邮箱提供商帮助文档里有,要么就和客服索要。二、添加解析操作步骤 1、联系邮箱提供商,获取邮箱解析地址记录。
|
移动开发 C++
|
关系型数据库 测试技术 数据库