C语言使用SMTP发送邮件

本文介绍了如何使用C语言通过SMTP协议发送邮件。详细讲解了SMTP的基本原理,包括发送邮件所需的关键命令,并且提到了邮件数据的Base64编码方式,用于确保账号和密码的安全。还列举了一个支持SMTP的邮箱的示例实现,帮助读者理解SMTP邮件发送的完整流程。

1 原理

  1. SMTP
    Simple Mail Transfer Protocol,简单邮件传输协议。简单发送邮件用到的基本命令如下表,注意每条命令以\r\n结尾。

    命令格式说明
    EHLOEHLO 发件人信息用于向服务器标明用户身份,可以为发件人服务器域名或者计算机名,例如EHLO YANG-PC,随意填写也能发送成功
    AUTH LOGINAUTH LOGIN邮箱认证,发送该命令后依次发送邮箱账号和密码(账号密码均使用Base64编码)
    MAIL FROMMAIL FROM:发件邮箱发件人邮件地址
    RCPT TORCPT TO:收件邮箱收件人邮件地址
    DATADATA准备开始发送邮件内容,发送该命令后发送邮件内容,注意邮件内容要按下文介绍的邮件数据格式
    QUITQUIT结束,返回250表示此次发送成功

    注意:每条命令以\r\n结尾。

    邮件数据格式包含邮件头和邮件体,格式如下(注意\r\n已经表示换行,以下示例写成多行只是为了格式清晰)

    From: "要显示的发件人名称"<发件人邮箱>\r\n
    To: "要显示的收件人名称"<收件人邮箱>\r\n
    Subject: 邮件主题\r\n\r\n
    邮件内容\r\n.\r\n
  2. Base64编码
    邮箱账号和密码采用Base64编码,Base64编码将连续的三个字符按一定规则转换为4个字符,达到不能直接看出文本内容的效果。具体规则如下

    • 3个字符以二进制形式连接起来一共24位,然后平均分成4部分得到4个6位的数,查编码表可得四个字符,这4个字符便是转换结果
    • 转换前字符总数可能不是3的倍数,若剩余一个字符,则将该字符二进制形式后面加4个0,得到两个6位二进制数,查得两个字符,然后再加两个“=”
    • 若剩余两个字符,则将两个字符二进制形式连接起来并在后面加2个0,然后平均分成3部分得到3个6位二进制数,查得3个字符,然后再加一个“=”

    示例

    原字符串ASCII码二进制二进制重组编码索引编码结果
    “ABC”0x41 0x42 0x4301000001 01000010 01000011010000 010100 001001 00001116 20 09 03“QUJD”
    “+-*/”0x2B 0x2D 0x2A 0x2F00101011 00101101 00101010 00101111001010 110010 110100 101010 001011 11000010 50 52 42 11 48“Ky0qLw==”
    “20158”0x32 0x30 0x31 0x35 0x3800110010 00110000 00110001 00110101 00111000001100 100011 000000 110001 001101 010011 10000012 35 00 49 13 19 32“MjAxNTg=”

    Base64编码表

    0123456789101112
    ABCDEFGHIJKLM
    13141516171819202122232425
    NOPQRSTUVWXYZ
    26272829303132333435363738
    abcdefghijklm
    39404142434445464748495051
    nopqrstuvwxyz
    525354555657585960616263
    0123456789+/


  3. 支持SMTP的邮箱

    邮箱SMTP服务器端口说明
    126邮箱smtp.126.com25现在需要开通客户端授权密码等认证才能开通SMTP服务,不过我N年前注册的直接就开通的
    163邮箱smtp.163.com25应该和126一样的
    sina邮箱smtp.sina.com25开启SMTP服务需要绑定手机

2 实现

一个完整示例

#include <windows.h>
#include <stdio.h>
#include <WinSock.h>
#pragma comment(lib, "ws2_32.lib")

#define SMTP_BUFSIZE 1024

void EncodeBase64(char* src, char* encode);                             // Base64编码
int SendMail(char *from, char *pwd, char* to, char* title, char* text); // 发送邮件

int main()
{
    WSADATA WSAData;
    WSAStartup(MAKEWORD(2, 2), &WSAData);
    SendMail("xxxxxxxx@126.com", "xxxxxxxx", "xxxxxxxx@hotmail.com", "Hello", "This is a test mail");
    WSACleanup();
    return 0;
}

// Base64编码
void EncodeBase64(char* src, char* encode)
{
    char base64_table[] = {
        'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 
        'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 
        'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 
        'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', '='};
    int len = strlen(src);
    int i = 0;
    for(i = 0; i < len/3; i++)
    {
        int temp = byte(src[3*i+2]) + (byte(src[3*i+1]) << 8) + (byte(src[3*i]) << 16);
        encode[4*i] = base64_table[(temp & 0xfc0000) >> 18];
        encode[4*i+1] = base64_table[(temp & 0x3f000) >> 12];
        encode[4*i+2] = base64_table[(temp & 0xfc0) >> 6];
        encode[4*i+3] = base64_table[temp & 0x3f];
    }
    encode[4*i] = 0;
    if (1 == len % 3)
    {
        int temp = byte(src[3*i]) << 16;
        encode[4*i] = base64_table[(temp & 0xfc0000) >> 18];
        encode[4*i+1] = base64_table[(temp & 0x3f000) >> 12];
        encode[4*i+2] = base64_table[64];
        encode[4*i+3] = base64_table[64];
        encode[4*i+4] = 0;
    }
    else if (2 == len % 3)
    {
        int temp =(byte(src[3*i+1]) << 8) + (byte(src[3*i]) << 16);
        encode[4*i] = base64_table[(temp & 0xfc0000) >> 18];
        encode[4*i+1] = base64_table[(temp & 0x3f000) >> 12];
        encode[4*i+2] = base64_table[(temp & 0xfc0) >> 6];
        encode[4*i+3] = base64_table[64];
        encode[4*i+4] = 0;
    }
}

// 发送邮件
int SendMail(char *from, char *pwd, char* to, char* title, char* text)
{
    char buf[SMTP_BUFSIZE] = {0};
    char account[128] = {0};
    char password[128] = {0};
    // 连接邮件服务器
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(25);
    hostent* phost = gethostbyname("smtp.126.com");
    memcpy(&addr.sin_addr.S_un.S_addr, phost->h_addr_list[0], phost->h_length);
    SOCKET sockfd = socket(PF_INET, SOCK_STREAM, 0);
    if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
    {
        printf("smtp socket() error");
        return 1;
    }
    if (connect(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr)) < 0)
    {
        printf("smtp connect() error");
        return 2;
    }
    // EHLO
    char pcname[128] = {0};
    DWORD size = 128;
    GetComputerName(pcname, &size); // 获取计算机名
    sprintf_s(buf, SMTP_BUFSIZE, "EHLO %s\r\n", pcname);
    send(sockfd, buf, strlen(buf), 0);
    recv(sockfd, buf, SMTP_BUFSIZE, 0);
    // AUTH LOGIN
    sprintf_s(buf, SMTP_BUFSIZE, "AUTH LOGIN\r\n");
    send(sockfd, buf, strlen(buf), 0);
    recv(sockfd, buf, SMTP_BUFSIZE, 0);
    // 邮箱账号
    EncodeBase64(from, account);
    sprintf_s(buf, SMTP_BUFSIZE, "%s\r\n", account);
    send(sockfd, buf, strlen(buf), 0);
    recv(sockfd, buf, SMTP_BUFSIZE, 0);
    // 密码
    EncodeBase64(pwd, password);
    sprintf_s(buf, SMTP_BUFSIZE, "%s\r\n", password);
    send(sockfd, buf, strlen(buf), 0);
    recv(sockfd, buf, SMTP_BUFSIZE, 0);
    // MAIL FROM 发件人
    sprintf_s(buf, SMTP_BUFSIZE, "MAIL FROM:<%s>\r\n", from);
    send(sockfd, buf, strlen(buf), 0);
    recv(sockfd, buf, SMTP_BUFSIZE, 0);
    // RCPT TO 收件人
    sprintf_s(buf, SMTP_BUFSIZE, "RCPT TO:<%s>\r\n", to);
    send(sockfd, buf, strlen(buf), 0);
    recv(sockfd, buf, SMTP_BUFSIZE, 0);
    // DATA 准备开始发送邮件内容
    sprintf_s(buf, SMTP_BUFSIZE, "DATA\r\n");
    send(sockfd, buf, strlen(buf), 0);
    recv(sockfd, buf, SMTP_BUFSIZE, 0);
    // 发送邮件内容
    sprintf_s(buf, SMTP_BUFSIZE, 
        "From: \"yang\"<%s>\r\nTo: \"test\"<%s>\r\nSubject: %s\r\n\r\n%s\r\n.\r\n", from, to, title, text);
    send(sockfd, buf, strlen(buf), 0);
    recv(sockfd, buf, SMTP_BUFSIZE, 0);
    // QUIT 结束
    sprintf_s(buf, SMTP_BUFSIZE, "QUIT\r\n");
    send(sockfd, buf, strlen(buf), 0);
    recv(sockfd, buf, SMTP_BUFSIZE, 0);
    if (strlen(buf) >= 3)
    {
        if (buf[0] == '2' && buf[1] == '5' && buf[2] == '0')
        {
            printf("sucess\n");
        }
    }
    closesocket(sockfd);
    system("pause");
    return 0;
}

本文原文链接 http://blog.youkuaiyun.com/yanglx2022/article/details/47759069

主要功能: 1、可以发送带附件的邮件,附件可以是多个,附件大小限制由发送方服务器而定,暂未测试具体为多少MB 2、邮件内容和主题可以是空,但当有附件时,主题取第一个附件的文件名(不含扩展名) 3、密码验证均为base64加密 4、邮件正文和附件的数据传送方式,均为base64 5、自动解析发件箱的SMTP服务器 压缩包文件简介: base.c:包含一些基本的函数,其中有一些在此程序中并未用到,只要使用了其中的base64加密算法 mail.c:包含邮件发送、数据读取、编码转换、smtp服务器连接、ip解析等函数 mailsend.c:包含main的c源文件,mail.exe则是根据mailsend.c、mail.c、base.c编译成的,具体编译方 法可参考makefile libbase.a:make之后生成的静态库 moontalk.cfg:base.c用到的配置文件,可能没用,放在这里进攻阅读参考 mail.cfg:自定义用户的配置文件,可用可不用,用作读代码的参考 mail.exe:邮件发送的执行文件,仅有命令行模式完善了,逐步输入(直接双击)的方式还不完善 b64.exe:base64加密解密的小工具,仅供参考,mail.cfg中用到密码的地方,可以使这个工具得到。 makefile:工程编译链接文件 注意:在本地使用mingw环境开发,遵循ANSI C标准,本地有系统的工程库,但是上传的时候,把这些文件 都放在一起了,可以先参考makefile进行工程调整,如果有任何问题,请发送邮箱moontalk@yeah.net, 技术交流,不胜感激。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值