发送邮件

#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <unistd.h>
#include <netinet/in.h>
#include <limits.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <ctype.h>
 
bool CFileUploadFTP::SendAlarmMail(std::string file, uint32_t size, std::string recv_mail_addr)
{
	const char* szServer = "smtp.126.com";
	short nPort = 25;
	int sockfd;
	struct sockaddr_in server_addr;
	struct hostent *host;
	//取得主机IP地址
	if((host=gethostbyname(szServer))==NULL)
	{
		logn_error(5,"gethostname error: [%s]", strerror(errno));
		return false;
	}
	//建立SOCKET连接
	if((sockfd=socket(AF_INET,SOCK_STREAM,0)) == -1)
	{
		logn_error(5,"Socket Error:[%s]>", strerror(errno));
		return false;
	}
	// 客户程序填充服务端的信息
	bzero(&server_addr,sizeof(server_addr));
	server_addr.sin_family=AF_INET;
	server_addr.sin_port=htons(nPort);
	server_addr.sin_addr=*((struct in_addr *)host->h_addr);
	//客户程序发起连接请求
	if(connect(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1)
	{
		logn_error(5,"Connect Error:[%s]>", strerror(errno));
		return false;
	}

	char szBuf[1024] = {0};
	char buf[256] = {0};
	char server[256] = {0};
	gethostname(server,256);
	strcpy(buf, "HELO ");
	strcat(buf, szServer);
	strcat(buf, " \r\n");
	// Wait for a reply
	recv(sockfd,szBuf,sizeof(szBuf)+1,0); 
	send(sockfd, buf, strlen(buf), 0);

	recv(sockfd, szBuf, sizeof(szBuf), 0); 
	//发送准备登陆信息
	send(sockfd, "AUTH LOGIN\r\n", strlen("AUTH LOGIN\r\n"), 0);
	recv(sockfd, szBuf, sizeof(szBuf), 0); 
	//发送用户名和密码,必须用base64进行转码,对于126邮箱来说用户名是@前面的字符串
	send(sockfd,"转码后的用户名\r\n", strlen("转码后的密码\r\n"),0); 
	recv(sockfd, szBuf, sizeof(szBuf),0);
	send(sockfd,"WnlqXzIxNjU5Mw==\r\n", strlen("WnlqXzIxNjU5Mw==\r\n"), 0); 
	recv(sockfd, szBuf, sizeof(szBuf),0); 
	logn_info(5,"\nrecv login rerurn info:%s", szBuf);
	//发送[发送邮件]的邮箱,该邮箱要与用户名一致,否则发送不成功
	send(sockfd,"MAIL FROM: <xxxx@126.com>\r\n",strlen("MAIL FROM: <xxxx@126.com>\r\n"),0);
	recv(sockfd, szBuf, sizeof(szBuf), 0);
	//发送[接收邮件]的邮箱
	char dat_buf[1024] = {0};
	sprintf(dat_buf, "RCPT TO: <%s>\r\n", recv_mail_addr.c_str());
	send(sockfd,dat_buf,strlen(dat_buf),0);
	recv(sockfd, szBuf, sizeof(szBuf), 0); 
	//发送主题  
	//char mail_data[1024] = {0}; 
	sprintf(dat_buf, "From: \"xxxx.126.com\"<xxxx@126.com>\r\nTo: %s\r\nSubject:file:[%s],size:[%d]\r\n", \
	        recv_mail_addr.c_str(), file.substr(12).c_str(), size);
        //告诉邮件服务器,准备发送邮件内容
	send(sockfd,"DATA\r\n", strlen("DATA\r\n"),0);
	//发送邮件标题
	send(sockfd,dat_buf, strlen(dat_buf),0);
	//发送邮件内容
	//send(sockfd,"hello 126\r\n", strlen("hello 126\r\n"),0);
	//发送邮件结束
	send(sockfd,"\r\n.\r\n", strlen("\r\n.\r\n"),0);
	//接收邮件服务器返回信息
	recv(sockfd,szBuf,sizeof(szBuf),0);  
	//发送结束会话标识
	send(sockfd,"QUIT\r\n", strlen("QUIT\r\n"),0);
	recv(sockfd,szBuf,sizeof(szBuf),0);  
	logn_info(5,"Data received QUIT: %s", szBuf);
	close(sockfd);

	return true;
}

  

调用方法:SendAlarmMail("/opt/dis/v1/work/personnel/150627B0016000200024_RYFZ_20210119171951.TXT", 1024000, "xxxx.163.com");
 
 
 
方法2:基于libesmtp库发送邮件。
安装方法: yum install -y  libesmtp-devel
编译方法 g++ xxx.cpp  -o mail  -lesmtp
 
#define _XOPEN_SOURCE  // 定义系统环境,使符合X/Open标准
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#include <getopt.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
#include <stdarg.h>
#include <libesmtp.h>  // 包含libesmtp函数库
 
int main()
{
	smtp_session_t session;// 定义SMTP会话
	smtp_message_t message;// 定义SMTP消息结构
	struct sigaction sa;   // 该结构包含收到信号后程序的行为
	const smtp_status_t *status;// 用于保存SMTP状态
	char buf[128];// 用于文件的缓冲区
	FILE *fp; // 文件标识符
	// 创建SMTP会话
	if((session = smtp_create_session ()) == NULL)
	{                     
		fprintf (stderr, "创建会话失败:%s\n",smtp_strerror (smtp_errno (), buf, sizeof(buf)));
		return 1;
	}
	// 从SMTP会话中接受消息,判断是否成功
	if((message = smtp_add_message (session)) == NULL) 
	{ 
		fprintf (stderr, "服务器无应答:%s\n",smtp_strerror (smtp_errno (), buf, sizeof(buf)));
		return 1;
	}
	sa.sa_handler = SIG_IGN;                                       // 避免进程僵死
	sigemptyset(&sa.sa_mask);                                      // 初始化信号集
	sa.sa_flags = 0;                                               // 使信息不被阻塞
	sigaction (SIGPIPE, &sa, NULL);                                // 设置信号行为
	smtp_set_server (session, "127.0.0.1:25");                     // 设置SMTP服务器地址与端口
	smtp_set_reverse_path (message, "GitHub");              // 设置发送者邮箱地址
	smtp_set_header (message, "To", NULL, NULL);                   // 使邮件头包含目的地邮箱地址
	smtp_set_header (message, "Subject", " 你大爷的!");            // 使邮件头包含主题
	smtp_set_header_option (message, "Subject", Hdr_OVERRIDE, 1);  // 使用默认的邮件头设置
	fprintf(stderr, "%s\n", "SMTP服务器设置成功");
	if ((fp = fopen ("./mail.cpp", "r")) == NULL) 
	{
		perror("打开文件失败");
		return 1;
	}
	smtp_set_message_fp (message, fp);                               //将文件中的内容作为邮件消息内容
	smtp_add_recipient (message,"xxxx@126.com");                     // 为消息添加一个容器,接受者邮箱地址
	//smtp_add_recipient (message,"chuanwen.gu@thermofisher.com"); 
	if (!smtp_start_session (session))
	{                              //连接SMTP服务器传送邮件
		fprintf (stderr, "SMTP server problem %s\n",
		smtp_strerror (smtp_errno (), buf, sizeof buf));
	}
	else
	{
		status = smtp_message_transfer_status (message);             // 获取发送状态
		printf ("%d %s", status->code,(status->text != NULL) ? status->text : "\n");
	}
	smtp_destroy_session (session);                                  // 结束SMTP会话
	if(fp != NULL) fclose(fp);                                       // 关闭文件
   
	return 0;
}

  

 
方法3:windows vs2017下一个发送邮件类
Csmtp.h
#pragma once

#include <iostream>  
#include <string>  
#include <vector>
#include <fstream> 
#include <WinSock2.h>  //适用平台 Windows

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

class Csmtp
{
public:
    Csmtp();
    ~Csmtp();

    Csmtp( int _port, //端口25 
        string _domain,     //域名
        string _user,       //发送者的邮箱
        string _pass,       //密码
        string _target)     //目标邮箱
        :m_port(_port), m_domain(_domain), m_user(_user), m_pass(_pass), m_target(_target) {};
    bool CReateSocket();
    void setTitle(string tem) { m_title = tem; }
    void setContent(string tem) { m_content = tem; }
    /*发送附件*/
    int SendAttachment(SOCKET &sockClient); 
    int SendMail();
    void addfile(string str) { filename.push_back(str); }
private:
    int m_port;
    string m_domain;
    string m_user;
    string m_pass;
    string m_target;
    string m_title;  //邮件标题
    string m_content;  //邮件内容
    HOSTENT* m_pHostent;
    SOCKET m_sockClient;  //客户端的套接字
    vector <string> filename;  //存储附件名的向量
};

Csmtp.cpp

#include "stdafx.h"
#include "Csmtp.h"
#include <Ws2tcpip.h>

static const char base64Char[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

Csmtp::Csmtp()
{
}

Csmtp::~Csmtp()
{
}

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 &m_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(m_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(m_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;
    }

    m_title = namebuf;// 邮件标题
    m_content = ip_list; //主机ip
    m_sockClient = socket(AF_INET, SOCK_STREAM, 0); //建立socket对象  
    m_pHostent = gethostbyname(m_domain.c_str()); //得到有关于域名的信息
    if (m_pHostent == NULL)
    {
        printf("创建连接失败,也许没联网!\n");
        return false;
    }

    return true;
}

int Csmtp::SendMail()
{
    char *ecode;
    char buff[512];  //recv函数返回的结果
    int err = 0;
    string message;
    SOCKADDR_IN addrServer;  //服务端地址
    addrServer.sin_addr.S_un.S_addr = *((DWORD *)m_pHostent->h_addr_list[0]); //得到smtp服务器的网络字节序的ip地址     
    addrServer.sin_family = AF_INET;
    addrServer.sin_port = htons(m_port); //连接端口25 
    //int connect (SOCKET s , const struct sockaddr FAR *name , int namelen );
    err = connect(m_sockClient, (SOCKADDR*)&addrServer, sizeof(SOCKADDR));   //向服务器发送请求  
    cout << "connect:" << err << endl;
    //telnet smtp.126.com 25 连接服务器结束
    buff[recv(m_sockClient, buff, 500, 0)] = '\0';
    cout << "connect:" << buff << endl;

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

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

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

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

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

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

    //  发送附件
    SendAttachment(m_sockClient);

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

    message = "QUIT\r\n";
    send(m_sockClient, message.c_str(), message.length(), 0);
    buff[recv(m_sockClient, buff, 500, 0)] = '\0';
    cout << "Send mail is finish:" << buff << endl;

    return 0;
}

测试文件:

main.cpp

// sendMail.cpp: 定义控制台应用程序的入口点。
#include "stdafx.h"
#include <iostream>
#include "Csmtp.h"

using namespace std;

int main()
{
    Csmtp mail(
        25,
        "smtp.126.com",
        "yaxt_alarm_mail@126.com",// 来源邮箱
        "xxxx",   //密码
        "xxxx@126.com" //目标邮箱
    );

    if (!mail.CReateSocket())
    {
        cout << "ReateSocket failed!" << endl;
        return -1;
    }

    //标题默认是主机名,内容默认是ip
    mail.setTitle("test mail tow");
    mail.setContent("this is content.");
    //附件路径错误,不影响邮件正文的发送。
    //mail.addfile("test.jpg"); //添加附件
    //mail.addfile("test2.png"); //添加附件
    mail.SendMail(); //类主函数
    system("pause");

    return 0;
}

 

 
 
 
 
 
 
 
posted @ 2021-01-19 18:33  嫦娥5号  阅读(106)  评论(0)    收藏  举报