用C写一发送邮件的程序Word格式.docx
《用C写一发送邮件的程序Word格式.docx》由会员分享,可在线阅读,更多相关《用C写一发送邮件的程序Word格式.docx(6页珍藏版)》请在冰豆网上搜索。
此命令开始一个邮件传输处理,最终完成将邮件数据传送到一个或多个邮箱中。
RCPTTO:
<forward-path><CRLF><forward-path>标识各个邮件接收者的地址
DATA<CRLF>
接收SMTP将把其后的行为看作邮件数据去处理,以<CRLF>.<CRLF>标识数据的结尾。
REST<CRLF>退出/复位当前的邮件传输
NOOP<CRLF>要求接收SMTP仅做OK应答。
(用于测试)
QUIT<CRLF>要求接收SMTP返回一个OK应答并关闭传输。
VRFY<string><CRLF>验证指定的邮箱是否存在,由于安全因素,服务器多禁止此命令。
EXPN<string><CRLF>验证给定的邮箱列表是否存在,扩充邮箱列表,也常禁止使用。
HELP<CRLF>查询服务器支持什么命令注:
<CRLF>为回车、换行,ASCII码分别为13、10(十进制)。
SMTP协议的每一个命令都会返回一个应答码,应答码的每一个数字都是有特定含义的,如第一位数字为2时表示命令成功;
为5表失败;
3表没有完成。
一些较复杂的邮件程序利用该特点,首先检查应答码的首数字,并根据其值来决定下一步的动作。
下面将SMTP的应答码列表如下:
应答码说明
501参数格式错误
502命令不可实现
503错误的命令序列
504命令参数不可实现
211系统状态或系统帮助响应
214帮助信息
220<domain>服务就绪
221<domain>服务关闭
421<domain>服务未就绪,关闭传输信道
250要求的邮件操作完成
251用户非本地,将转发向<forward-path>
450要求的邮件操作未完成,邮箱不可用
550要求的邮件操作未完成,邮箱不可用
451放弃要求的操作;
处理过程中出错
551用户非本地,请尝试<forward-path>
452系统存储不足,要求的操作未执行
552过量的存储分配,要求的操作未执行
553邮箱名不可用,要求的操作未执行
354开始邮件输入,以"
."
结束
554操作失败
在应用程序中使用SMTP协议 SMTP协议的会话流程 在进行程序设计之前有必要弄清SMTP协议的会话流程,其实前面介绍的内容已经可以大致勾勒出用SMTP发送邮件的框架了,对于一次普通的邮件发送,其过程大致为:
先建立TCP连接,随后客户端发出HELLO命令以标识发件人自己的身份,并继续由客户端发送MAIL命令,如服务器应答为"
OK"
,可继续发送RCPT命令来标识电子邮件的收件人,在这里可以有多个RCPT行,而服务器端则表示是否愿意为收件人接受该邮件。
在双方协商结束后,用命令DATA将邮件发送出去,其中对表示结束的"
也一并发送出去。
随后结束本次发送过程,以QUIT命令退出。
下面通过一个实例,从langrui@发送邮件到renping@来更详细直观地描述此会话流程:
R:
220SimpleMailTransferServiceReady
S:
HELLO
R:
250
<langrui@>
250OK
<renping@>
DATA
354Startmailinput;
endwith"
<CRLF>.<CRLF>"
&
#8230;
QUIT
221Serviceclosingtransmissionchannel 邮件的格式化 由于电子邮件结构上的特殊性,在传输时是不能当作简单的文本来直接处理的,而必须按照一定的格式对邮件头和邮件体进行格式化处理之后才可以被发送。
需要进行格式化的部分主要有:
发件人地址、收件人地址、主题和发送日期等。
在RFC文档的RFC822里对邮件的格式化有详尽的说明,有关详情请参阅该文档。
下面通过VC++6.0按照RFC822文档规定将格式化邮件的部分编写如下(部分代码):
//邮件头准备
strTemp=_T("
From:
"
)+m_strFrom;
file:
//发件人地址
add_header_line((LPCTSTR)strTemp);
To:
)+m_strTo;
//收件人地址
m_tDateTime=m_tDateTime.GetCurrentTime();
//发送时间
Data:
);
strTemp+=m_tDateTime.Format("
%a,%d%b%y%H:
%M:
%S%Z"
Subject:
)+m_strSubject;
//主题
file:
//邮件头结束
m_strHeader+=_T("
\r\n"
//邮件体准备
if(m_strBody.Right
(2)!
=_T("
))file:
//确认最后以回车换行结束
m_strBody+=_T("
其中add_header_line(LPCTSTRszHeaderLine)函数用于把szHeaderLine指向的字串追加到m_strHeader后面。
其中,格式化后的邮件头保存在m_strHeader里,格式化后的邮件体保存在m_strBody中。
由Socket套接字为SMTP提供网络通讯基础 许多网络程序都是采用Socket套接字实现的,对于一些标准的网络协议如HTTP、FTP和SMTP等协议的编程也是基于套接字程序的,只是端口号不再是随意设定而要由协议来指定,比如HTTP端口在80、FTP是21,而SMTP则是25。
Socket只是提供在指定的端口上同指定的服务器从事网络上的通讯能力,至于客户和服务器之间是如何通讯的则由网络协议来规定,这对于套接字是完全透明的。
因此可以使用Socket套接字为程序提供网络通讯的能力,而对于网络通讯连路建立好之后采取什么样的通讯应答则要按SMTP协议的规定去执行了。
Socket套接字网络编程方面的文章资料非常丰富,限于本文篇幅,在此不再赘述,有关详情请参阅相关文档。
为简便起见,没有采用编写较复杂的WindowsSocketsAPI进行编程,而是使用经过较好封装的MFC的CSocket类。
在正式使用套接字之前,也要先用AfxSocketInit()函数对套接字进行初始化,然后用Create()创建套接字对象,并由该套接字通过Connect()建立同邮件服务器的连接。
如果一切正常,再后续的工作中就是遵循SMTP协议的约定来使用Send()、Receive()函数来发送SMTP命令和接收邮件服务器发来的应答码以完成对邮件的传送。
SMTP会话应答的实现 在同邮件服务器建立好链路连接后就可以按前面介绍过的会话流程进行程序设计了,对于SMTP命令的发送,可按命令格式将其组帧完毕后用CSocket类的Send()函数将其发送到服务器,并通过CSocket类的Receive()函数接收从邮件服务器发来的应答码,并根据SMTP协议的应答码表对其做出响应的处理。
下面是用于接收应答码的函数get_response()的部分实现代码:
BOOLCSMTP:
:
get_response(UINTresponse_expected)//输入参数为希望的应答码
{
//m_wsSMTPServer为CSocket的类对象,调用Receive()将应答码接收到缓存
//response_buf中
m_wsSMTPServer.Receive(response_buf,RESPONSE_BUFFER_SIZE)
sResponse=response_buf;
sscanf((LPCTSTR)sResponse.Left(3),_T("
%d"
),&
amp;
response);
pResp=&
response_table[response_expected];
//检验收到的应答码是否是所希望得到的
if(response!
=pResp->nResponse)
//不相等的话进行错误处理
returnFALSE;
}
returnTRUE;
} 会话的各个部分比较类似,都是命令--应答方式,而且均成对出现,下面是本文的重点也是实现的关键部分--在程序控制下完成对SMTP命令的格式化以及对命令的发送和对邮件服务器应答码的检验处理:
//格式化并发送HELLO命令,并接收、验证服务器应答码
gethostname(local_host,80);
sHello.Format(_T("
HELO%s\r\n"
),local_host);
m_wsSMTPServer.Send((LPCTSTR)sHello,sHello.GetLength());
if(!
get_response(GENERIC_SUCCESS))file:
//检验应答码是否为250
//格式化并发送MAIL命令,并接收、验证服务器应答码
sFrom.Format(_T("
MAILFrom:
<%s>\r\n"
),(LPCTSTR)msg->m_strFrom);
m_wsSMTPServer.Send((LPCTSTR)sFrom,sFrom.GetLength());
//格式化并发送RCPT命令,并接收、验证服务器应答码
sEmail=(LPCTSTR)msg->m_strTo;
sTo.Format(_T("
),(LPCTSTR)sEmail);
m_wsSMTPServer.Send((LPCTSTR)sTo,sTo.GetLength());
if(!
get_response(GENERIC_SUCCESS))file:
//格式化并发送DATA命令,并接收、验证服务器应答码
sTemp=_T("
DATA\r\n"
m_wsSMTPServer.Send((LPCTSTR)sTemp,sTemp.GetLength());
get_response(DATA_SUCCESS))file:
//检验应答码是否为354
//发送根据RFC822文档规定格式化过的邮件头
m_wsSMTPServer.Send((LPCTSTR)msg->m_strHeader,msg->m_strHeader.GetLength());
//发送根据RFC822文档规定格式化过的邮件体
sTemp=msg->m_strBody;
if(sTemp.Left(3)==_T("
.\r\n"
))
)+sTemp;
while((nPos=sTemp.Find(szBad))>-1)
sCooked=sTemp.Mid(nStart,nPos);
sCooked+=szGood;
sTemp=sCooked+sTemp.Right(sTemp.GetLength()-(nPos+nBadLength));
//发送内容数据结束标志"
,并检验返回应答码
\r\n.\r\n"
get_response(GENERIC_SUCCESS))//检验应答码是否为250
到此为止,已基本在程序中体现出了SMTP协议的会话流程,能在Socket套接字所提供的网络通讯能力基础之上实现以SMTP命令和SMTP应答码为基本会话内容的通讯交互过程,从而最终实现SMTP协议对电子邮件的发送。
结论 电子邮件类软件作为Internet上的应用软件,其设计开发必须符合Internet上成熟的技术规范(如RFC文档系列规范)和相关协议(如POP、SMTP、IMAP以及LDAP等)。
只有在遵循了上述规范和协议的基础上进行编程才能真正实现邮件类软件产品和服务的开放性和标准化。
本文着重对SMTP协议及其在VC++编程中的应用做了介绍,并按照SMTP协议对电子邮件的发送进行了开放性和标准性较好的程序设计。
本文所述程序在Windows98下,由MicrosoftVisualC++6.0编译通过。