1 原理
SMTP
Simple Mail Transfer Protocol,简单邮件传输协议。简单发送邮件用到的基本命令如下表,注意每条命令以\r\n结尾。命令 格式 说明 EHLO EHLO 发件人信息 用于向服务器标明用户身份,可以为发件人服务器域名或者计算机名,例如 EHLO YANG-PC,随意填写也能发送成功AUTH LOGIN AUTH LOGIN 邮箱认证,发送该命令后依次发送邮箱账号和密码(账号密码均使用Base64编码) MAIL FROM MAIL FROM:发件邮箱 发件人邮件地址 RCPT TO RCPT TO:收件邮箱 收件人邮件地址 DATA DATA 准备开始发送邮件内容,发送该命令后发送邮件内容,注意邮件内容要按下文介绍的邮件数据格式 QUIT QUIT 结束,返回250表示此次发送成功 注意:每条命令以
邮件数据格式包含邮件头和邮件体,格式如下(注意\r\n结尾。\r\n已经表示换行,以下示例写成多行只是为了格式清晰)From: "要显示的发件人名称"<发件人邮箱>\r\n To: "要显示的收件人名称"<收件人邮箱>\r\n Subject: 邮件主题\r\n\r\n 邮件内容\r\n.\r\nBase64编码
邮箱账号和密码采用Base64编码,Base64编码将连续的三个字符按一定规则转换为4个字符,达到不能直接看出文本内容的效果。具体规则如下- 3个字符以二进制形式连接起来一共24位,然后平均分成4部分得到4个6位的数,查编码表可得四个字符,这4个字符便是转换结果
- 转换前字符总数可能不是3的倍数,若剩余一个字符,则将该字符二进制形式后面加4个0,得到两个6位二进制数,查表得两个字符,然后再加两个“=”
- 若剩余两个字符,则将两个字符二进制形式连接起来并在后面加2个0,然后平均分成3部分得到3个6位二进制数,查表得3个字符,然后再加一个“=”
示例
Base64编码表原字符串 ASCII码 二进制 二进制重组 编码索引 编码结果 “ABC” 0x41 0x42 0x43 01000001 01000010 01000011 010000 010100 001001 000011 16 20 09 03 “QUJD” “+-*/” 0x2B 0x2D 0x2A 0x2F 00101011 00101101 00101010 00101111 001010 110010 110100 101010 001011 110000 10 50 52 42 11 48 “Ky0qLw==” “20158” 0x32 0x30 0x31 0x35 0x38 00110010 00110000 00110001 00110101 00111000 001100 100011 000000 110001 001101 010011 100000 12 35 00 49 13 19 32 “MjAxNTg=” 0 1 2 3 4 5 6 7 8 9 10 11 12 A B C D E F G H I J K L M 13 14 15 16 17 18 19 20 21 22 23 24 25 N O P Q R S T U V W X Y Z 26 27 28 29 30 31 32 33 34 35 36 37 38 a b c d e f g h i j k l m 39 40 41 42 43 44 45 46 47 48 49 50 51 n o p q r s t u v w x y z 52 53 54 55 56 57 58 59 60 61 62 63 0 1 2 3 4 5 6 7 8 9 + / 支持SMTP的邮箱
邮箱 SMTP服务器 端口 说明 126邮箱 smtp.126.com 25 现在需要开通客户端授权密码等认证才能开通SMTP服务,不过我N年前注册的直接就开通的 163邮箱 smtp.163.com 25 应该和126一样的 sina邮箱 smtp.sina.com 25 开启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
本文介绍了如何使用C语言通过SMTP协议发送邮件。详细讲解了SMTP的基本原理,包括发送邮件所需的关键命令,并且提到了邮件数据的Base64编码方式,用于确保账号和密码的安全。还列举了一个支持SMTP的邮箱的示例实现,帮助读者理解SMTP邮件发送的完整流程。
1277





