在以前接触的项目中,一直都是在做网站时用到了发送mail 的功能,在asp 和.net 中都有相关的发送mail 的类,
实现起来非常简单。最近这段时间因工作需要在C++ 中使用发送mail
的功能,上网搜了一大堆资料,终于得以实现,总结自己开发过程中碰到的一些问题,希望对需的人有所帮助, 由于能力有限,
文中不免有些误解之处, 望大家能指正!!
其实,使用C++ 发送mail 也是很简的事, 只需要了解一点SMTP 协议和socket 编程就OK 了,
网络上也有很多高人写好的mail 类源码,有兴趣的朋友可以下载看看.
1.SMTP 常用命令简介
1). SMTP 常用命令
HELO/EHLO 向服务器标识用户身份
MAIL 初始化邮件传输
mail from:
RCPT 标识单个的邮件接收人;常在MAIL 命令后面
可有多个rcpt to:
DATA 在单个或多个RCPT 命令后,表示所有的邮件接收人已标识,并初始化数据传输,以. 结束。
VRFY 用于验证指定的用户/ 邮箱是否存在;由于安全方面的原因,服务器常禁止此命令
EXPN 验证给定的邮箱列表是否存在,扩充邮箱列表,也常被禁用
HELP 查询服务器支持什么命令
NOOP 无操作,服务器应响应OK
QUIT 结束会话
RSET 重置会话,当前传输被取消
如你对SMTP 命令不了解,可以用telnet 命令登陆到smtp 服务器用help 命令进行查看:
Normal 0 7.8 磅 0 2 false false false
MicrosoftInternetExplorer4
220 tdcsw.maintek.corpnet.asus ESMTP Sendmail 8.13.8/8.13.8; Sat, 9
Jan 10:
45:09 +0800
help
214-2.0.0 This is sendmail
214-2.0.0 Topics:
214-2.0.0HELOEHLOMAILRCPTDATA
214-2.0.0RSETNOOPQUITHELPVRFY
214-2.0.0EXPNVERBETRNDSNAUTH
214-2.0.0STARTTLS
214-2.0.0 For more info use "HELP ".
214-2.0.0 To report bugs in the implementation see
214-2.0.0/email-addresses.html
214-2.0.0 For local information send email to Postmaster at your
site.
214 2.0.0 End of HELP info
2).SMTP 返回码含义
*邮件服务返回代码含义
*500格式错误,命令不可识别(此错误也包括命令行过长)
*501参数格式错误
*502命令不可实现
*503错误的命令序列
*504命令参数不可实现
*211系统状态或系统帮助响应
*214帮助信息
*220服务就绪
*221服务关闭传输信道
*421服务未就绪,关闭传输信道(当必须关闭时,此应答可以作为对任何命令的响应)
*250要求的邮件操作完成
*251用户非本地,将转发向
*450要求的邮件操作未完成,邮箱不可用(例如,邮箱忙)
*550要求的邮件操作未完成,邮箱不可用(例如,邮箱未找到,或不可访问)
*451放弃要求的操作;处理过程中出错
*551用户非本地,请尝试
*452系统存储不足,要求的操作未执行
*552过量的存储分配,要求的操作未执行
*553邮箱名不可用,要求的操作未执行(例如邮箱格式错误)
*354开始邮件输入,以.
结束
*554操作失败
*535用户验证失败
*235用户验证成功
*334等待用户输入验证信息 for next
connection>;
3) SMTP 命令应用
我们下需使用telnet 命令实现smtp 邮件的发送,具体操作如下:
220 ESMTP Sendmail 8.13.8/8.13.8; Wed, 23 Dec
18
:18:18 +0800
HELO tdcsw
250 Hello x-128-101-1-240.ahc.umn.edu [128.101.1.240],
pleased to meet you
MAIL FROM:lily@
250 2.1.0 lily@... Sender ok
RCPR TO:sam@
250 2.1.5 carven@... Recipient
ok
DATA
354 Enter mail, end with "." on a line by itself
SUBJECT:HELLO
HI:
HAR are you?
.
250 2.0.0 nBNAIIG4000507 Message accepted for
delivery
quit
221 2.0.0 tdcsw.maintek.corpnet.asus closing
connection
Connection to host lost.
2.用C++ 实现Mail 发送
为了便于理解, 在此就不封装Mail 类了, 而是以过程式函数方式给出.
1). 首先需要建立TCP 套接字, 连接端口依服务器而定,SMTP 服务默认端口为25, 我们以
默认端口为例
WSADATA wsaData;
intSockFD;
WSAStartup(MAKEWORD(2,2), &wsaData);
SockFD = socket(AF_INET, SOCK_STREAM,
IPPROTO_TCP);
ServAddr.sin_family = AF_INET;
ServAddr.sin_addr.s_addr = inet_addr
(“192.168.1.1”);//192.168.1.1 为服务器地址
ServAddr.sin_port = htons(25);
connect(SockFD, (struct sockaddr *)&ServAddr,
sizeof(ServAddr));
2). 发送SMTP 命令及数据
const char HEADER[] = "HELO smtpSrv\r\n"
"MAIL FROM:
sender@\r\n"
"RCPT TO:
recv@\r\n"
"DATA\r\n"
"FROM:
sender@\r\n"
"TO:
recv@\r\n"
"SUBJECT: this is a
test\r\n"
"Date: Fri, 8 Jan
16:12:30\r\n"
"X-Mailer: shadowstar's mailer\r\n"
"MIME-Version:
1.0\r\n"
"Content-type:
text/plain\r\n\r\n";
//send HEADER
send(SockFD, HEADER, strlen(HEADER), 0);
const char CONTENT[]="this is content.\r\n";
//send CONTENT
send(SockFD, CONTENT, strlen(CONTENT), 0);
send(SockFD, ".\r\n", strlen(".\r\n"),
0);//end
send(SockFD, "QUIT\r\n", strlen("QUIT\r\n"), 0);
//quit
mail 发送的功能基本上就完成了, 当然, 如果是应用的话还是需要很多改动的地方的,
比如说添加附件等.
3). 附件功能
要使用SMTP 发送附件, 需要对SMTP 头信息进行说明, 改变Content-type 及为每一段正文添加BOUNDARY 名,
示例如下:
"DATA\r\n"
"FROM:
sender@\r\n"
"TO:
recv@\r\n"
"SUBJECT: this is a
test\r\n"
"Date: Fri, 8 Jan
16:12:30\r\n"
"X-Mailer: shadowstar's mailer\r\n"
"MIME-Version:
1.0\r\n"
"Content-type: multipart/mixed;
boundary="#BOUNDARY#"\r\n\r\n";
// 正文
"--#BOUNDARY#\r\n"
"Content-Type: text/plain;
charset=gb2312\r\n"
"Content-Transfer-Encoding:
quoted-printable\r\n"
邮件正文……….
// 附件
"\r\n--#BOUNDARY#\r\n"
"Content-Type:
application/octet-stream; name=att.txt\r\n"
"Content-Disposition:
attachment; filename=att.txt\r\n"
"Content-Transfer-Encoding:
base64\r\n"
"\r\n"
附件正信息(base64 编码)…..
Base64 编码函数在网络上很容易找到, 这里就不给出源码了, 如需要支持HTML 格式而又不知道如何写这些头信息,
可以用outlook 或foxmail 写一封支持HTML 格式的mail, 查看其原文信息,
依照相同的格式发送就行了.
4). 实现抄送及密送
在SMTP 命令集中并没有RCPT CC 或RCPT BCC 相关命令,
那要如何来实现抄送和密送功能呢?
在网络上找到这样一句话: “ 所有的接收者协商都通过RCPT TO 命令来实现,如果是BCC
,则协商发送后在对方接收时被删掉信封接收者”, 开始一直不明白这句话是什么意思? 后来通看查看foxmail
的邮件原文发现:
Date: Wed, 6 Jan 12:11:48 +0800
From: "carven_li" < carven_li @>
To: "carven"
Cc: "sam" ,
"yoyo"
BCC: "clara"
Subject: t
X-mailer: Foxmail 5.0 [cn]
Mime-Version: 1.0
Content-Type: multipart/mixed;
boundary="=====001_Dragon237244850520_====="
才恍然大悟, 所谓的” 协商” 应该就是指发送方在Data 中指定哪些为CC, 哪些为BCC, 默认情况下什么都不写,
只发送第一个RCPT TO 的mail, 其他的都被过滤掉.