爬虫实现股票分析七之邮件发送


准备知识点:

1.邮件授权码

2.telnet服务开启

3.base64编码

4.smtp协议简单使用

5.windows socket编程

详细流程:

1.邮件授权码在:设置-pop3/SMTP/IMAP里设置

2.telnet服务开启:控制面板-程序-启动或关闭windows功能-telnet客户端

3.base64编码

Base64是网络上最常见的用于传输8Bit字节码的编码方式之一

第一步,将待转换的字符串每三个字节分为一组,每个字节占8bit,那么共有24个二进制位。
第二步,将上面的24个二进制位每6个一组,共分为4组。
第三步,在每组前面添加两个0,每组由6个变为8个二进制位,总共32个二进制位,即四个字节。
第四步,根据Base64编码对照表(见下图)获得对应的值  

--最终将要传输的字符都变成可见字符,这样防止经过网络上一些只支持可见字符的计算机而出问题,这边的加密和解密方式时固定的,因此对安全性没有啥特别好的保护

4.smtp协议简单使用      --注意账号和密码都要经常base64加密,并且密码是授权码

在cmd窗口里:

telnet

open smtp.163.com 25

即会进入以下界面,并进行图片里操作即会发出邮件:

密码被涂掉了,请谅解,ZWZmb3J0c2NvZGVwYW5kYQ== 即是 efforscodepanda的base64加密而来

发往163自己是因为第三方不携带自身的邮箱地址会报554操作,而且发给自己的邮箱也能查的到

5.windows socket编程  --具体注意点很多,基础知识部分请大家自己学习

这边特别感谢:https://blog.csdn.net/sinat_36219858/article/details/80439782

源码:

#include   
#include <string>  
#include 
#include   

#include   //适用平台 Windows

#pragma  comment(lib, "ws2_32.lib") /*链接ws2_32.lib动态链接库*/ 
// POP3服务器(端口:110) Csmtp服务器(端口:25) 
using namespace std;
class Csmtp
{

    int port;
    string domain;
    string user;
    string pass;
    string target;
    string title;  //邮件标题
    string content;  //邮件内容


    HOSTENT* pHostent;
    SOCKET sockClient;  //客户端的套接字
    vector <string> filename;  //存储附件名的向量

public:

    Csmtp(
        int _port, //端口25
        string _domain,     //域名
        string _user,       //发送者的邮箱
        string _pass,       //密码
        string _target)     //目标邮箱
        :port(_port), domain(_domain), user(_user), pass(_pass), target(_target){};//内容 
    bool CReateSocket();
    void setTitle(string tem){ title = tem; }
    void setContent(string tem){ content = tem; }

    int SendAttachment(SOCKET &sockClient);
    int SendMail();
    void addfile(string str){ filename.push_back(str); }

};
#include "Csmtp.h"
//#include //异常类
static const char base64Char[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
char* base64Encode(char const* origSigned, unsigned origLength)
{
    unsigned char const* orig = (unsigned char const*)origSigned; // in case any input bytes have the MSB set  
    if (orig == NULL) return NULL;

    unsigned const numOrig24BitValues = origLength / 3;
    bool havePadding = origLength > numOrig24BitValues * 3;
    bool havePadding2 = origLength == numOrig24BitValues * 3 + 2;
    unsigned const numResultBytes = 4 * (numOrig24BitValues + havePadding);
    char* result = new char[numResultBytes + 3]; // allow for trailing '/0'  

    // Map each full group of 3 input bytes into 4 output base-64 characters:  
    unsigned i;
    for (i = 0; i < numOrig24BitValues; ++i)
    {
        result[4 * i + 0] = base64Char[(orig[3 * i] >> 2) & 0x3F];
        result[4 * i + 1] = base64Char[(((orig[3 * i] & 0x3) << 4) | (orig[3 * i + 1] >> 4)) & 0x3F];
        result[4 * i + 2] = base64Char[((orig[3 * i + 1] << 2) | (orig[3 * i + 2] >> 6)) & 0x3F];
        result[4 * i + 3] = base64Char[orig[3 * i + 2] & 0x3F];
    }

    // Now, take padding into account.  (Note: i == numOrig24BitValues)  
    if (havePadding)
    {
        result[4 * i + 0] = base64Char[(orig[3 * i] >> 2) & 0x3F];
        if (havePadding2)
        {
            result[4 * i + 1] = base64Char[(((orig[3 * i] & 0x3) << 4) | (orig[3 * i + 1] >> 4)) & 0x3F];
            result[4 * i + 2] = base64Char[(orig[3 * i + 1] << 2) & 0x3F];
        }
        else
        {
            result[4 * i + 1] = base64Char[((orig[3 * i] & 0x3) << 4) & 0x3F];
            result[4 * i + 2] = '=';
        }
        result[4 * i + 3] = '=';
    }

    result[numResultBytes] = '\0';
    return result;
}


int Csmtp::SendAttachment(SOCKET &sockClient) /*发送附件*/
{
    for (std::vector<string>::iterator iter = filename.begin(); iter != filename.end(); iter++)
    {
        cout << "Attachment is sending··· " << endl;

        string path = *iter;
        ifstream ifs(path, ios::in | ios::binary); //'或链接2个属性,以输入、二进制打开'
        if (false == ifs.is_open())
        {
            cout << "无法打开文件!" << endl;
            return 1;
        }

        string sendstring;
        sendstring = "--@boundary@\r\nContent-Type: application/octet-stream; name=\"1.jpg\"\r\n";
        sendstring += "Content-Disposition: attachment; filename=\"1.jpg\"\r\n";
        sendstring += "Content-Transfer-Encoding: base64\r\n\r\n";
        send(sockClient, sendstring.c_str(), sendstring.length(), 0);

        //infile.read((char*)buffer,sizeof(数据类型));

        // get length of file:
        ifs.seekg(0, ifs.end);
        int length = ifs.tellg();
        ifs.seekg(0, ifs.beg);
        cout << "length:" << length << endl;
        // allocate memory:
        char * buffer = new char[length];
        // read data as a block:
        ifs.read(buffer, length);
        ifs.close();
        char *pbase;
        pbase = base64Encode(buffer, length);
        delete[]buffer;
        string str(pbase);
        delete[]pbase;
        str += "\r\n";
        int err = send(sockClient, str.c_str(), strlen(str.c_str()), 0);

        if (err != strlen(str.c_str()))
        {
            cout << "附件发送出错!" << endl;
            return 1;
        }
    }
    return 0;
}


bool Csmtp::CReateSocket()
{
    WSADATA wsaData;
    WORD wVersionRequested = MAKEWORD(2, 1);
    //WSAStarup,即WSA(Windows SocKNDs Asynchronous,Windows套接字异步)的启动命令
    int err = WSAStartup(wVersionRequested, &wsaData);
    cout << "WSAStartup(0:successful):" << err << endl;

    char namebuf[128];    //获得本地计算机名
    string ip_list;
    if (0 == gethostname(namebuf, 128))
    {
        struct hostent* pHost;  //获得本地IP地址
        pHost = gethostbyname(namebuf);  //pHost返回的是指向主机的列表
        for (int i = 0; pHost != NULL&&pHost->h_addr_list[i] != NULL; i++)
        {
            string tem = inet_ntoa(*(struct in_addr *)pHost->h_addr_list[i]);
            ip_list += tem;
            ip_list += "\n";
        }
    }
    else
    {
        cout << "获取主机信息失败..." << endl;
    }
    //////////////////////////////////////////////////////////////////////////
    title = namebuf;// 邮件标题
    content = ip_list; //主机ip

    sockClient = socket(AF_INET, SOCK_STREAM, 0); //建立socket对象  

    pHostent = gethostbyname(domain.c_str()); //得到有关于域名的信息

    if (pHostent == NULL)
    {
        printf("创建连接失败,也许没联网!\n");
        return false;
    }

    return true;
}


int Csmtp::SendMail()
{
    char *ecode;

    char buff[500];  //recv函数返回的结果
    int err = 0;
    string message; //

    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(port); //连接端口25 
    //int connect (SOCKET s , const struct sockaddr FAR *name , int namelen );
    err = connect(sockClient, (SOCKADDR*)&addrServer, sizeof(SOCKADDR));   //向服务器发送请求  
    cout << "connect:" << err << endl;
    //telnet smtp.126.com 25 连接服务器结束
    buff[recv(sockClient, buff, 500, 0)] = '\0';
    cout<<"connect:"<endl;

    message = "ehlo 126.com\r\n";
    send(sockClient, message.c_str(), message.length(), 0);

    buff[recv(sockClient, buff, 500, 0)] = '\0';
    cout<<"helo:"<endl;

    message = "auth login \r\n";
    send(sockClient, message.c_str(), message.length(), 0);
    buff[recv(sockClient, buff, 500, 0)] = '\0';
    cout<<"auth login:"<endl;
    //上传邮箱名
    message = user;
    ecode = base64Encode(message.c_str(), strlen(message.c_str()));
    message = ecode;
    message += "\r\n";
    delete[]ecode;
    send(sockClient, message.c_str(), message.length(), 0);
    buff[recv(sockClient, buff, 500, 0)] = '\0';
    cout<<"usrname:"<endl;
    //上传邮箱密码
    message = pass;
    ecode = base64Encode(message.c_str(), strlen(message.c_str()));
    message = ecode;
    delete[]ecode;
    message += "\r\n";
    send(sockClient, message.c_str(), message.length(), 0);
    buff[recv(sockClient, buff, 500, 0)] = '\0';
    cout<<"password:"<endl;

    message = "mail from:<" + user + ">\r\nrcpt to:<" + target + ">\r\n";
    send(sockClient, message.c_str(), message.length(), 0);
    buff[recv(sockClient, buff, 500, 0)] = '\0';
    cout<<"mail from: "<endl;
    buff[recv(sockClient, buff, 500, 0)] = '\0';
    cout<<"rcpt to: "<endl;

    message = "data\r\n";//data要单独发送一次
    send(sockClient, message.c_str(), message.length(), 0);
    buff[recv(sockClient, buff, 500, 0)] = '\0';
    cout<<"data: "<endl;
    ///-----------------------------------------DATA-------------------------------------
    //要使用Csmtp 发送附件, 需要对Csmtp 头信息进行说明, 改变Content-type 及为每一段正文添加BOUNDARY 名,
    cout << "-------------------DATA------------------------" << endl;
    //
    message = "from:" + user + "\r\nto:" + target + "\r\nsubject:" + title + "\r\n";
    message += "MIME-Version: 1.0\r\n";
    message += "Content-Type: multipart/mixed;boundary=@boundary@\r\n\r\n";
    send(sockClient, message.c_str(), message.length(), 0);

    //  正文
    message = "--@boundary@\r\nContent-Type: text/plain;charset=\"gb2312\"\r\n\r\n" + content + "\r\n\r\n";
    send(sockClient, message.c_str(), message.length(), 0);

    //------------------------------------------------------------------------------------------------
    //  发送附件

    SendAttachment(sockClient);

    /*发送结尾信息*/
    message = "--@boundary@--\r\n.\r\n";
    send(sockClient, message.c_str(), message.length(), 0);
    buff[recv(sockClient, buff, 500, 0)] = '\0';
    cout<<"end_qwertyuiop:"<endl;

    message = "QUIT\r\n";
    send(sockClient, message.c_str(), message.length(), 0);
    buff[recv(sockClient, buff, 500, 0)] = '\0';
    cout << "Send mail is finish:" << buff << endl;
    return 0;
}
#include "Csmtp.h"
#pragma comment(lib, "Kernel32.lib")  

int main()
{

    Csmtp mail(
        25,
        "smtp.163.com",
        "effortscodepanda@163.com",// 来源邮箱
        "xxxxx",   //密码
        "effortscodepanda@163.com" //目标邮箱
        );

    if (!mail.CReateSocket())
    {
        cout << "ReateSocket failed!" << endl;
        return -1;//
    }
    //标题默认是主机名,内容默认是ip
    mail.setTitle("test mail");
    mail.setContent("this is content. \n yinh");
    //附件路径错误,不影响邮件正文的发送。
    mail.addfile("test.jpg"); //添加附件
    //mail.addfile("test2.png"); //添加附件

    mail.SendMail(); //类主函数
    system("pause");
    return 0;
}

这边附件位置是相对位置,因此附件放在工程所在目录,我的test.jpg 是放在 D:\Documents\Visual Studio 2010\Projects\SendEmailTest\SendEmailTest目录的

最终邮件效果:

写代码的小熊猫 ,:) 每天积累一点点,开开心心每一天~