C语言发送email

C语言发送email   应用:分布式评测系统中检测到连接断开后向管理员发送邮件。 一、认证方式  ESMTP(ExtensionSMTP)即认证的邮件传输方式,是邮件服务器系统为了限制非本系统的正式用户利用本系统散发垃圾邮件或其他不当行为而开设的一项安全认证服务。它与传统的SMTP方式相比,主要的不同有两点:  1)支持8-bitMIME格式的编码。  2)支持用户身份的验证。  多了一道用

大家好,又见面了,我是你们的朋友全栈君。

    应用:分布式评测系统中检测到连接断开后向管理员发送邮件。

 

一、 认证方式

  ESMTP(Extension SMTP)即认证的邮件传输方式,是邮件服务器系统为了限制非本系统的正式用户利用本系统散发垃圾邮件或其他不当行
为而开设的一项安全认证服务。它与传统的SMTP方式相比,主要的不同有两点:
  1) 支持8-bit MIME格式的编码。
  2) 支持用户身份的验证。
  多了一道用户身份的验证手续,验证之后的邮件发送过程与传统的SMTP方式一致。为了方便用户的使用,绝大多数的ESMTP服务器都继承了
POP3服务器的帐号和密码设置体系,也就是说收发邮件都用同一个帐号和密码。

  根据[RFC 2554]规范,SMTP的认证功能主要是增加了AUTH命令。AUTH命令有多种用法,而且有多种认证机制。AUTH支持的认证机制主要有LOGIN,CRAM-MD5[注1]等。LOGIN应该是大多数免费邮件服务器都支持的,网易与新浪都支持。下面主要针对LOGIN方式进行介绍,其它方式请根据相应的RFC 规范进行修改。
     几乎所有的ESMTP服务器都继承了POP3服务器的账号和密码设置体系,也就是说收发邮件用相同的账号和密码。当然,也可以用不同的账号和密码,但那样无论是电子邮件服务提供商的维护还是用户的使用都会很麻烦,故而很少采用。

LOGIN 方式口令-应答过程如下(S:表示服务器返回,C:表示客户端发送)
  1. C: AUTH LOGIN
  2. S: 334 dXNlcm5hbWU6      // dXNlcm5hbWU6是username:的BASE64编码
  3. C: dXNlcm5hbWU6
  4. S: 334 cGFzc3dvcmQ6     // cGFzc3dvcmQ6是password:的BASE64编码
  5. C: cGFzc3dvcmQ6
  6. S: 235 Authentication successful.
  (1). 为客户端向服务器发送认证指令。
  (2). 服务端返回base64编码串,成功码为334。编码字符串解码后为”username:”,说明要求客户端发送用户名。
  (3). 客户端发送用base64编码的用户名,此处为”username:”。
  (4). 服务端返回base64编码串,成功码为334。编码字符串解码后为”password:”,说明要求客户端发送用户口令。
  (5). 客户端发送用base64编码的口令,此处为”password:”。
  (6). 成功后,服务端返回码为235,表示认证成功可以发送邮件了

二:BASE64编码原理

  Base64编码其实是将3个8位字节转换为4个6位字节,( 3*8 = 4*6 = 24 ) 这4个六位字节 其实仍然是8位,只不过高两位被设置为0. 当一个字
节只有6位有效时,它的取值空间为0~63。
事实上,0~63之间的ASCII码有许多不可见字符,所以应该再做一个映射,映射表为
‘A‘ ~ ‘Z‘ ? ASCII(0 ~ 25)
‘a’ ~ ‘z‘ ? ASCII(26 ~ 51)
‘0’ ~ ‘9‘ ? ASCII(52 ~ 61)
‘+‘ ? ASCII(62)
‘/‘ ? ASCII(63)
这样就可以将3个8位字节,转换为4个可见字符。
具体的字节拆分方法为:(图(画得不好,领会精神 :-))
aaaaaabb ccccdddd eeffffff   //abcdef其实就是1或0,为了看的清楚就用abcdef代替
00aaaaaa 00bbcccc 00ddddee 00ffffff
注:上面的三个字节位原文,下面四个字节为Base64编码,其前两位均为0。

这样拆分的时候,原文的字节数量应该是3的倍数,当这个条件不能满足时,用全零字节补足,转化时Base64编码用=号代替,这就是为什么有
些Base64编码以一个或两个等号结束的原因,但等号最多有两个,因为:如果F(origin)代表原文的字节数,F(remain)代表余数,则
F(remain) = F(origin) MOD 3 成立。
所以F(remain)的可能取值为0,1,2.
如果设 n = [F(origin) – F(remain)] / 3
当F(remain) = 0 时,恰好转换为4*n个字节的Base64编码。
当F(remain) = 1 时,由于一个原文字节可以拆分为属于两个Base64编码的字节,为了让Base64编码是4的倍数,所以应该为补2个等号。
当F(remain) = 2 时,由于两个原文字节可以拆分为属于3个Base64编码的字节,同理,应该补上一个等号。

 

三、发送步骤

1)获得邮件服务器的地址。调用gethostbyname(),将smtp.sina.com作为参数传入,保存返回的 hostent结构的地址,用它的h_addr_list[0]来初始化sockaddr_in结构的sin_addr.S_un.S_addr成员,端口写成25,这是邮件服务标准端口。

 

2)TCP Socket连接。

 

3)使用SMTP来通信了。该通信是个同步的过程,遵守一发一收的规则,连接上后先接收服务器的反馈信息,然后发送“HELO [信息]/r/n”表明身份,命令EHLO和后面的信息要有空格,信息可以什么信息都不加,接收后继续发送“AUTH LOGIN/r/n”,接收后发送用户名(即邮箱地址中@前边的部分),再发送密码,此时服务器返回是否验证成功的信息,若成功则返回代码为235的信息,否则返回535,注意有可能由于服务器的繁忙而导致验证失败,并不是用户名和密码的问题,所以失败后要继续从头开始,连接-发送-接收-验证,另外,用户名和密码采用base64编码。
       验证后就可以发送具体的邮件信息了。首先发送发件人,“MAIL FROM: <用户名@邮件服务器>/r/n”。其次发送收件人,这个可是要起作用的,发送 “RCPT TO: <目的邮箱>”,要发送给几个人,就发送几个“RCPT TO: <目的邮箱>”,然后发送“DATA/r/n”表示要发送具体数据了,数据格式为:邮件头+邮件体
       邮件头:
       From: “想要显示的发件人”<想要显示的邮箱名>/r/n
       To: <想要显示的收件人邮箱地址>/r/n
       Subject: 主题名/r/n/r/n  此处的两个/r/n表示邮件头结束
       邮件体:
       邮件内容
       结束标志为/r/n./r/n
       将这些信息组成一个字符串发送出去就可以了,最后发送“QUIT /r/n”断开连接。
      至此,邮件发送程序便编写完成了。

4)断开TCP连接。

 

四、关键代码

void sendemail(char *mailsrv, char *mailfrom, char *mailto, char *body)
{

    int sockfd = 0,i=0;
    struct sockaddr_in their_addr = {0};
    char buf[1500] = {0};
    char rbuf[1500] = {0};
    char login[128] = {0};
    char pass[128] = {0};

    struct hostent *hp = gethostbyname(mailsrv);
       
    memset(&their_addr, 0, sizeof(their_addr));
    their_addr.sin_family = AF_INET;
    their_addr.sin_port = htons(25);
    their_addr.sin_addr.s_addr = *(int*)(*hp->h_addr_list);

    sockfd = open_socket((struct sockaddr *)&their_addr);
    memset(rbuf,0,1500);
    while(recv(sockfd, rbuf, 1500, 0) == 0)
    {

        printf(“reconnect…/n”);
        sleep(2);
        close(sockfd);
        sockfd = open_socket((struct sockaddr *)&their_addr);
        memset(rbuf,0,1500);
    }
    printf(“%d.%s/n”,i++, rbuf);
   
    //1.EHLO
    memset(buf, 0, 1500);
    sprintf(buf, “EHLO caoli/r/n”);
    send(sockfd, buf, strlen(buf), 0);
    memset(rbuf, 0, 1500);
    recv(sockfd, rbuf, 1500, 0);
    printf(“%d.%s/n”,i++, rbuf);
    //2.AUTH LOGIN
    memset(buf, 0, 1500);
    sprintf(buf, “AUTH LOGIN/r/n”);
    send(sockfd, buf, strlen(buf), 0);
    printf(“%s/n”, buf);
    memset(rbuf, 0, 1500);
    recv(sockfd, rbuf, 1500, 0);
    printf(“%d.%s/n”,i++, rbuf);
    //3.USER
    memset(buf, 0, 1500);
    sprintf(buf,”caoli”);
    memset(login, 0, 128);
    base64(login, buf, strlen(buf));
    sprintf(buf, “%s/r/n”, login);
    send(sockfd, buf, strlen(buf), 0);
    printf(“%s/n”, buf);
    memset(rbuf, 0, 1500);
    recv(sockfd, rbuf, 1500, 0);
    printf(“%d.%s/n”,i++, rbuf);
    //4.PASSWD
    sprintf(buf, “33060808”);
    memset(pass, 0, 128);
    base64(pass, buf, strlen(buf));
    sprintf(buf, “%s/r/n”, pass);
    send(sockfd, buf, strlen(buf), 0);
    printf(“%s/n”, buf);
    memset(rbuf, 0, 1500);
    recv(sockfd, rbuf, 1500, 0);
    printf(“%d.%s/n”,i++, rbuf);
    //5.MAIL FROM
    memset(buf, 0, 1500);
    sprintf(buf, “MAIL FROM:<%s>/r/n”,mailfrom);
    send(sockfd, buf, strlen(buf), 0);
    memset(rbuf, 0, 1500);
    recv(sockfd, rbuf, 1500, 0);
    printf(“%d.%s/n”,i++, rbuf);
    //6.MAIL TO …(multiple destination)
    sprintf(buf, “RCPT TO:<%s>/r/n”, mailto);
    send(sockfd, buf, strlen(buf), 0);
    memset(rbuf, 0, 1500);
    recv(sockfd, rbuf, 1500, 0);
    printf(“%d:%d.%s/n”,__LINE__,i++, rbuf);
/*
    sprintf(buf, “RCPT TO:starryheavens@hotmail.com/r/n”);
    send(sockfd, buf, strlen(buf), 0);
    memset(rbuf, 0, 1500);
    recv(sockfd, rbuf, 1500, 0);
    printf(“%d.%s/n”,i++, rbuf);
*/
    //7.DATA
    sprintf(buf, “DATA/r/n”);
    send(sockfd, buf, strlen(buf), 0);
    memset(rbuf, 0, 1500);
    recv(sockfd, rbuf, 1500, 0);
    printf(“%d.%s/n”,i++, rbuf);
    //8.CONTENT
    sprintf(buf, “%s/r/n./r/n”, body);
    send(sockfd, buf, strlen(buf), 0);
    memset(rbuf, 0, 1500);
    recv(sockfd, rbuf, 1500, 0);
    printf(“%d.%s/n”,i++, rbuf);
    //9.QUIT
    sprintf(buf, “QUIT/r/n”);
    send(sockfd, buf, strlen(buf), 0);
    memset(rbuf, 0, 1500);
    recv(sockfd, rbuf, 1500, 0);
    printf(“%d.%s/n”,i++, rbuf);

    close(sockfd);
    return;
}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/131877.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • pycharm怎么进行断点调试_pycharm怎么设置断点调试

    pycharm怎么进行断点调试_pycharm怎么设置断点调试PyCharm作为IDE,断点调试是必须有的功能。否则,我们还真不如用纯编辑器写的快。【运行】和【调试】前的设置,请看文章1.添加断点断点的添加如下图所示在代码前面左键单机即可 2.调试断点点击那个绿色的甲虫图标,进行断点调试。点击后,会运行到第一个断点。会显示该断点之前的变量信息。 点击StepOver或者按F8,我们继续往下运行,到下一个断点,按钮…

  • navicat15.0.22注册激活码【在线注册码/序列号/破解码】

    navicat15.0.22注册激活码【在线注册码/序列号/破解码】,https://javaforall.cn/100143.html。详细ieda激活码不妨到全栈程序员必看教程网一起来了解一下吧!

  • 京东活动+自动运行脚本+签到2021.05.26更新

    京东活动+自动运行脚本+签到2021.05.26更新先下载Node.js运行环境https://nodejs.org/en下载14.15.4版本。WIN7下载12.20的版本https://nodejs.org/dist/latest-v12.x/node-v12.20.1-x64.msi。只需要一路安装就可以。检测是否安装成功:点击开始-运行-cmd(win+R),打开dos,输入“node–version”检查Node.js版本:只要有显示就说明没问题了。每天大概300-400个京豆,有时候现金红包会有6元左右。下载脚本包:https://..

  • token实现验证登录(token如何使用)

    1.场景还原可能还有很多小伙伴对token概念朦朦胧胧,今天笔者以项目中的用户登录的token验证需求跟大家讲讲其中的来龙去脉,希望能够理清大伙的思路。2.需求分析这个需求可能早已是老生常谈,但我觉得它永远也不会过时①谷歌浏览器:login.html—->index.html;②然后复制index.html的地址在IE浏览器地址栏上,这时普遍网站都会使访问界面直接

  • C++stringstream的妙用「建议收藏」

    C++stringstream的妙用「建议收藏」1介绍C++引入了ostringstream、istringstream、stringstream这三个类,要使用他们创建对象就必须包含sstream.h头文件。istringstream类用于执行C++风格的串流的输入操作。ostringstream类用于执行C风格的串流的输出操作。strstream类同时可以支持C风格的串流的输入输出操作。istringstream类是从istre…

  • [原创]-数据仓库ETL开发

    [原创]-数据仓库ETL开发ETL开发概述ETL是数据仓库的后台,主要包含抽取、清洗、规范化、提交四个步骤,传统数据仓库一般分为四层模型。分层的作用:1.划分ETL阶段工作重心,便于管理2.降低开发和维护成本3.减…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号