146_一个SMTP客户端程序

1.无需注册登录,支付后按照提示操作即可获取该资料.
2.资料以网页介绍的为准,下载后不会有水印.资料仅供学习参考之用.
密 惠 保
2.3 相关协议的简单介绍
电子邮件系统经过几十年的发展,已经形成了较为完善的技术体系。在这个体系中,建立之初由RFC821文档规定的内容已经不能满足人们日益膨胀的需求。因此,制定出了一系列的相关协议来满足人们日益变化的需求。下面简单介绍几个相关的协议:
1、RFC2821文档:该文档是RFC821的较为完整的版本。没有改变任何原有的命令,只是简单的增加了内容。类似于821文档。目前大多数的SMTP协议实现参考的资料都是2821文档。它更全面,也更合理。
2、RFC2554文档:ESMTP(SMTP Service Extension for Authentication),它是对SMTP协议的扩展,确切来说是对于邮件传输中有关身份认证部分的规定。其中定义了新的关键字AUTH LOGIN,并且规定了身份认证的格式和验证的字符串采用的编码方式。下面给出一个简单的例子来说明验证的过程,其中S代表服务器,C代表客户端:
S: 220 smtp.example.com ESMTP server ready
[资料来源:THINK58.com]
C: EHLO jgm.example.com
S: 250-smtp.example.com
S: 250 AUTH LOGIN PLAIN。。。。。
C: AUTH LOGIN
S: 334 Username的BASE64编码
C: 用户名的BASE64编码
S: 334 Password的BASE64编码
C: 密码的BASE64编码
S: 235 Authentication successful.
当客户端发送连接请求到服务器时,服务器返回220应答码,说明目前服务器已经准备工作。客户端使用EHLO命令声明自己的身份。此时由于要进行身份验证,那么必须使用EHLO而不是HELO。服务器返回操作正确的250应答码。然后继续发送250应答码,要求客户端去进行身份验证。客户端发送AUTH LOGIN命令给服务器,服务器返回334要求客户端发送BASE64编码后的用户名。客户端发送BASE64编码后的用户名。服务器验证无误后,返回334代码,并要求客户端发送密码。客户端发送密码给服务器,等待服务器的验证结果,服务器返回235 Authentication successful信息给客户端。说明验证成功。可以继续发送邮件。
3、RFC2045文档:MIME(Multipurpose Internet Mail Extensions),是对邮件传送中的有关多媒体信息进行说明。(可以把其看成是822文档的全面升级版本,当然这种说法并不准确,因为它们共同点仅仅是都对内容格式进行说明。)在该协议中,规定了一些关键的格式字段如:A MIMI-Version header field , A Content-Type header field , A Content-Transfer-Encoding header field 以及两个扩展的字段 Content-ID 、Content- Description header fields等字段,并且对BASE64编码格式进行了说明。 copyright think58
[版权所有:http://think58.com]
3软件开发背景与环境
3.1 软件开发背景
作为计算机专业的学生,在课堂上学习到的仅仅是有关协议的理论知识,并没有分析并运用协议的实际经验。对于协议的相关知识只能停留在理论阶段。为了加深对SMTP协议的理解,全面掌握SMTP协议,提高动手能力和解决实际问题的能力,而提出了该课题。
本软件是为了学习目的而开发出的系统,采用VC++6.0作为开发平台,C++作为开发的语言。使用MFC提供的有关底层API,不依靠提供的组件,手动分析协议的工作流程。对发送和接收的数据进行分析,把文字描述的协议转化为计算机语言,并提供方便用户使用的操作界面。生成一个能够向任何实现了SMTP协议的邮件服务器发送邮件的客户端软件,服务器端可以任意选取(实现SMTP协议即可)。利用该客户端,用户可以方便的发送邮件。
3.2 软件开发环境
3.2.1系统环境
本系统采用的工具软件有:
OS:Windows 2003 Data Center Edition
IDE:Visual C++ 6.0企业版
3.2.2 工具简介
在本系统中,使用的开发工具为Visual C++6.0企业版。这是一个集成的开发环境。由软件业巨头微软强力推出。虽然年代久远,但是到目前为止,仍然是一款经典之作,在开发平台的阵营有着重要的地位。目前的C++开发阵地中,有两套application framework是最为著名的,一套是OWL,另一套是MFC。Visual C++支持的正是MFC的应用框架。在整个程序的构建时,也是使用了这个大名鼎鼎的Microsoft Foundation Class框架。它使得开发者能够把主要的精力都放在程序的逻辑部分,而不必为了那些琐屑的消息管理和消息传递或是消息循环而大伤脑筋,大大提高软件的开发效率。
当然它的作用不仅仅如此。支持MFC框架只是Visual C++IDE的一个特性而已。作为一款优秀的集成开发环境,它提供了很多方便开发的功能。例如:提供了工作区窗口,能够方便的对类、文件、资源进行视图化管理;提供了代码编辑时的提示功能,在开发较大的项目时,能够让开发者方便的找到自己想调用的方法;提供了断点机制,让程序调试更加方便、快捷;还提供了各种可视化的工程管理工具,大大提高了开发效率。
作为一个高度集成化的IDE,Visual C++ 6.0不仅仅提供了对C++语言的支持,而且能够用来编辑一些简单的图片和其他信息。对于开发者而言,不必把大量的时间都放在那些与程序无关的事情上,无疑是一件好事。除非你是个只喜欢把代码都CO、CO进去的时间浪费者,否则选择选择VC++这样的IDE无疑会对你有很大的帮助。
在该IDE中也提供了对开发人员来说最重要的工具之一,开发文档。目前很多的工具都自带了帮助文档,但微软的MSDN确实作的非常出色。就算你对这个工具丝毫不了解,也可以通过该文档快速的上手使用。 [来源:http://think58.com]
4 SMTP协议客户端软件设计与实现
4.1 需求分析与总体设计
4.1.1 功能分析
由本设计的题目可知,本设计的目的就是建立一款能够发送邮件的客户端软件。对本软件而言,应该具备如下功能:
1)可以保存用户输入数据。
2)可以根据用户输入的数据连接服务器,并进行身份验证。
3)可以对有关数据进行加密。
4)可以发送邮件信息(含附件)。
5)图形界面要信息完整、操作舒适、界面雅观。
根据以上分析,需要进行编码的操作有:
1) 从图形界面获取输入的数据的操作。
2) 根据MFC提供的API连接服务器,建立一条连接发送者和接收者的通道。
3) 提供BASE64的加密算法,能够对用户输入的数据进行加密。生成满足SMTP协议要求的数据。
4) 按照SMTP的要求,对邮件进行封装,生成满足协议要求的邮件。
5) 对邮件发送过程中,发送者与接收者之间的命令与应答码之间的关系进行分析。
6) 图形界面编程。
4.1.2 总体设计
目前流行的工作平台有很多,可以实现目标的编程语言也有多种。下面与流行的两种平台做比较并说明选择VC++的原因:
1、整个工程使用JAVA平台
从诞生至今,一路走来JAVA可以说是一帆风顺。已经超越了C++称为最受欢迎的语言之一。如果选择使用JAVA作为开发语言,并使用一种集成IDE,如:JBUILDER。调用下JAVA有关邮件发送的类库,那么整个的邮件发送过程就变成了对少数几个属性的设定问题,整个工程的主要任务就不再是对SMTP协议的分析,而仅仅是对有关界面的设计(而且JAVA的界面设计相对于其他的可视化来说好像有些不足,目前似乎只有NetBeans支持的比较好)。而且使用JAVA的条件是用户必须安装虚拟机,并且JAVA的执行速度在目前来看似乎也不太被看好。因此没有考虑使用JAVA来完成本设计。 think58好,好think58 [资料来源:http://www.THINK58.com]
2、整个工程使用C语言实现
论速度而言,除开低等的汇编,C语言绝对的独占熬头,并且C语言的语法简单,构建出的程序结构也清晰。结构化的程序设计方式,也让人自然而然的从上而下的去思考。其类库并且提供了大量的有关网络操作API,让用户能够方便的使用并获得所求的值。但C语言并没有提供太多有关图形设计方面的框架(或者是目前没有用于C语言的应用程序框架)。开发者可能会花大量的时间在分析事件的流程上,而不是程序的逻辑。如此一来,得不偿失。
因此,软件采用MFC为应用框架,配合IDE使用,能够自动提供出一套功能有限但设计结构清晰的标准Windows程序。使用开发语言为C++,是典型的面向对象设计语言。利用其面向对象的特性,在开发过程中能够方便的向软件添加功能。因此,在该程序的设计过程中,选取了C++作为开发语言,VC++6.0作为程序设计的IDE。在本软件构过程中,定义了CSMTP、CMailMessage、CMIMEMessage、CBASE64 四个工具类,给程序使用。整个程序就是使用了MFC提供的应用程序框架,并在其中添加了上述几个工具类,相互协调工作而得来。结构清晰,功能相对完备,既完成了预期的需求,也学习到了有关SMTP协议的知识。
4.2 各模块设计
4.2.1 实现SMTP协议的核心类库
think58
如上所述,目前与SMTP协议有关的核心类共四个,对于邮件的所有操作,均封装在四个工具类中。按照其完成的功能进行划分,每个工具类都可以作为一个子模块。四个子模块各守其则,分别代表了某一种功能或实体。
一、 CSMTP子模块:该模块封装了有关邮件发送过程的操作。使用该模块可以建立或断开与服务器的连接、向服务器发送消息、从服务器接受消息并分析得到的消息代码是否正确、在服务器返回错误消息时获取到该消息。对于SMTP的分析以及发送时发送端与接收端之间的会话,均由此类完成。该类提供了两个重要的程序接口Connect和TransmitMessage。其他模块只需调用其接口方法,并传递正确的参数,就可以方便的与服务器建立连接,并传送邮件内容。由于当前大多数SMTP服务器都已经要求用户进行身份验证,因此在该类中还封装了对发送者身份验证的操作。其类图如图4所示。
图4 CSMTP类类图
成员变量说明:
1) private BOOL m_bConnected:私有成员变量,表示当前是否与服务器连接。TRUE表示已连接;FALSE表示没有进行连接。
2) private UINT m_nPort:私有成员变量,表示与服务器连接的端口。在该软件中该值为25。表示使用服务器的25号端口。
3) private CString m_nSMTPServerHostName:私有成员变量,表示服务器的名称。例如:smtp.163.com。 think58 [资料来源:http://THINK58.com]
4) private CSocket m_SMTPServer:私有成员变量,表示连接到服务器的网络实体。
5) private CStringList * m_psErrorList:私有成员变量,是用于存放产生的错误消息的列表。
6) protected TCHAR* responseBuf:保护成员变量,用于存放服务器返回的消息。
7) protected static ResponseCode* responseTable[]:保护的静态成员变量,代表消息码与对应消息的映射表。
成员函数说明:
1) private BOOL GetResponse(UINT responseExpected):该方法根据获取的UINT类型的参数responseExpected来判断所进行的操作是否正确。在该方法中客户端接收从服务器发送来的消息,并进行解析,然后根据给定的参数responseExpected来进行判断。如果判断正确,那么返回TRUE;否则返回FALSE。
2) public void SetServerProperties(CString szSMTPServerName , UINT nPort=SMTP_PORT):该方法根据获得的字符串类型和UINT类型参数来设置要连接的服务器的名称以及端口号。其中szSMTPServerName是服务器的名称,nPort是端口号。其默认值为SMTP_PORT 25。
3) public CString GetLastError():通过调用该方法,能够获取服务器返回的错误信息。如果没有错误信息,则返回空。
4) public UINT GetPort():获取服务器的端口号。 copyright think58 [资料来源:www.THINK58.com]
5) public BOOL Connect():连接服务器方法。通过该方法,可以根据SetServerProperties方法设置的属性,连接到指定的服务器。如果连接成功则返回TRUE;否则返回FALSE。
6) public virtual BOOL TransmitMessgae(CMailMessage * msg):该方法的作用是根据传递进来的CMailMessage对象传送邮件。在该方法中封装了邮件发送的操作并对SMTP协议规定的命令和应答码的交互操作做了实现,而且与邮件发送有关的身份验证操作也在该方法中实现。本方法的参数msg应包含就是要发送的邮件的信息,如发送的来源、目的地、邮件的题目、发送时间、正文内容以及附件,均存储在msg对象中。在发送过程中,顺序发送命令:AUTH LOGIN、MAIL FROM 、RCPT TO(可重复多次)、DATA (结束符CRLF.CRLF)、QUIT,并在每次发送后设置一个接收指令,用于接收从服务器传回的数据,并进行分析。如果是预期的数据那么返回TRUE,标志发送成功;如果返回FALSE,标志发送失败。
7) private CString CookBody(CMailMessage * msg):该方法用于剔除在邮件正文以及邮件中与结束标志冲突的字符。在SMTP协议中规定:邮件正文以DATA命令开始,以“CRLF.CRLF”标志结束,为了避免在邮件正文中,出现上述的结束标志,必须在发送邮件前检测邮件,把邮件中所有的与邮件结束符号相同的字符替换为“CRLF..CRLF”,来避免在邮件发送时出现结束位置不明的错误。当邮件发送到服务器后,会自动的把邮件正文中的被替换的字符换回为原来的字符,从而保证邮件的正确性。该方法的参数为CMailMessage*类型的指针,代表一个邮件对象。该对象包含有关的邮件信息,方便在本方法中对要发送的邮件进行操作。
think58好,好think58 [来源:http://think58.com]
8) protected static ResponseCode* GetServerResponseMessage(UINT):该方法的作用是根据传递进来的消息码来返回一个与消息码对应的服务器消息。该方法采用查表的方式,根据邮件服务器发送的消息,来确定返回消息的具体信息,该方法主要在邮件操作出错时,提供错误信息。如果查表成功,返回一个合适的出错信息;如果查表失败,则返回未知错误。有关应答码和与应答码相关信息,均在本类的静态成员变量ResponseCode responseTable中做了规定。
二、 CMailMessage子模块:该模块用于表示邮件的所有内容,如:邮件的发送者、接收者、标题、正文以及附件。该类的设计借鉴了JAVA中JavaBean的定义方式,对私有数据进行封装只能通过对应的方法进行存取。使用本类提供了能够对邮件内容进行规范化设计的操作,使用这些操作能够得到满足SMTP协议规定的电子邮件。其类图如图5所示。 think58
[版权所有:http://think58.com]
copyright think58 [来源:http://www.think58.com]
[来源:http://think58.com]
think58
think58好,好think58
think58好,好think58
think58
[资料来源:http://THINK58.com]
内容来自think58
图5 CMailMessage类类图
成员变量说明:
1) public CPtrArray m_Attachments:公有成员变量,表示邮件的附件。使用MFC提供的CPtrArray类型变量,能够方便的存储多个附件的信息。
2) public CString m_AttachmentString:表示附件文件的名称,在给该类的对象添加有关的附件信息的时候使用。
3) protected CString m_sSubject、m_sFrom、m_sTo、m_sHeader、m_sBody、m_sPassword、CTime m_tDateTime:表示邮件的信息,分别为:邮件的标题、邮件发送者、邮件接收者、邮件头信息、邮件体正文、身份验证的密码以及邮件发送的时间。对于每个域均有一对对应的存取方法Get和Set来对其进行操作。这设计的方式参考了JAVA中的JavaBean的设计模式。方便调用者使用该类。
4) private CArray <CRecipient,CRecipient&> m_Recipients:私有成员变量。表示邮件接收者的一个数组。在SMTP协议中规定:一封邮件可以有多个接收者,每个接收者需要一个RCPT TO命令与之对应。该变量就是用于存储多个接收者的。
成员函数说明:
1) public int AddAttachMent(CString filename):该方法的作用是添加根据参数提供的文件名称,把该文件当成附件添加到当前的邮件中。返回值为邮件中已有的附件的个数。该方法有一个重载方法,参数为CMIMEMessage*,作用是把一个附件添加到当前邮件。返回值与该方法意义相同。 think58
2) public int GetAttachmentNum():该方法的作用是获取当前附件的个数。
3) public int GetNumberRecipients(RECIPIENTS_TYPE type=TO):作用是获取当前的接收者的数目。参数是接收者的类型,默认值是TO类型,意为普通的接收者。参数type是个enum RECIPIENT_TYPE类型的变量,有三个合法值,分别为:TO,CC,BCC。分别代表:普通接收者,抄送和密送。对于后两个值,本版本没有提供支持。
4) BOOL AddRecipient(LPCTSTR szEmailAddress , LPCTSTR szFriendlyName="" , RECIPIENTS_TYPE type=TO):作用是向邮件添加接收者信息。默认的接收者类型为TO,也是当前版本唯一能够支持的类型。szEmailAddress表示邮件的地址,szFriendlyName表示名称。
5) public void AddToHeader(CString sTemp):该方法作用是向邮件添加头信息。传递的参数为要向邮件添加的头信息。
6) public virtual void PrepareHeader():本方法的作用是根据对象本身的成员变量来生成邮件头。根据SMTP协议的规定,邮件的头包括:From、To(可重复多次)、Subject、Date、X-Mailer、MIME-Version: 、Content-type等多个域。这些域构成了邮件头的信息。只有正确包含上述域的邮件才能被邮件服务器接收。这些域的生成均包含在了该方法的实现中,通过调用该方法就可以获得一个满足SMTP协议要求的邮件头。 内容来自think58
7) public virtual void PrepareBody():该方法的作用是生成一个满足SMTP协议的邮件体。根据SMTP协议的规定:邮件的正文和附件消息均要满足固定的格式。并且邮件正文与附件、附件与附件之间要有正确的分隔标志。该方法中规定的邮件分隔标志为#BOUNDARY#,该标志由PrepareHeader()方法中定义。在方法中,根据当前添加的邮件附件的个数来添加标志。所有附件添加结束后,会添加一个为“--#BOUNDARY#--”的结束符号。标志邮件正文的结束。
三、 CMIMEMessage子模块:该模块用于表示邮件的附件。使用该类可以根据提供的文件名读取出正确的文件,保存文件名和标题。并提供了能够取出这些属性的方法。其类图如图6所示。 think58.com
[来源:http://think58.com]
think58.com [资料来源:http://www.THINK58.com]
think58好,好think58
[资料来源:http://think58.com]
内容来自think58 [版权所有:http://think58.com]
图6 CMIMEMessage类图
成员变量说明:
1) protected CString m_Filename:保护成员变量,表示附件所代表的文件的文件名。添加到邮件时使用。
2) protected CString m_FileContent、TCHAR * m_ContentBuffer:保护成员变量,表示附件所代表的文件的内容。
3) protected DWORD bufLen:保护成员变量,表示当前附件的长度,单位是字节。
成员函数说明:
1) public BOOL Attach(const CString &sFilename):该方法的作用是根据传递进来的参数文件名,来把本地文件中的内容读取出来,并添加到当前附件对象中。并且根据传递进来的参数,分别给bufLen、m_FileContent、m_Filename、m_Title等成员变量赋值。如果附件添加成功,则返回TRUE;否则返回FALSE。
2) public CString GetFilename()、CString GetTitle()、CString GetContent()、TCHAR * GetContentBuffer()、DWORD GetBufferLength():上述方法作用分别为获取附件名称、附件标题、附件内容(以字符串形势返回)、附件内容(以数组方式返回)、附件的长度。为类的调用者提供了统一的操作接口。
四、 CBASE64子模块:在SMTP的相关协议中规定,对于身份验证的用户名、密码、邮件的正文以及附件的内容均要采用BASE64的方式进行编码。该类就是对BASE64的算法进行的封装。提供了对两种数据源的加密操作和一种解密操作。其类图如图7所示。
本文来自think58 [来源:http://www.think58.com]
[来源:http://think58.com]think58.com
[版权所有:http://think58.com]
copyright think58
[版权所有:http://think58.com]
图7 CBASE64类图
成员变量说明:
1) static CString base64:该变量是归类所属的成员变量。表示BASE64编码所使用的字符表。