电子邮件客户端软件指导教师:叶树华0209040328银鹄821.引言1.1电子邮件简单介绍电子邮件(简称E-mai1)又称电子信箱、电子邮政,它是—种用电子手段提供信息交换的通信方式。它是全球多种网络上使用最普遍的一项服务。这种非交互式的通信,加速了信息的交流及数据传送,它是—个简易、快速的方法。通过连接全世界的Internet,实现各类信号的传送、接收、存贮等处理,将邮件送到世界的各个角落。到目前为止,可以说电子邮件是Internet资源使用最多的一种服务,E-mai1不只局限于信件的传递,还可用来传递文件、声音及图形、图像等不同类型的信息。例如比较著名的邮件收发软件有系统自带的OutLook,还有比较著名的第三方软件FoxMaiL,FastMail等等。1.2开发背景介绍当前流行的各大邮件客户端软件的除了最主要的收发信件之外,功能越来越复杂,但是人们平常真正用到的功能很少,很多功能尤其对于那些计算机知识相对缺乏的人来说,更加显得太过于华丽而不太实用。有鉴于此,在了解RFC底层协议的基础上,我们开发了这个各种功能相对简单实用的邮件客户端程序,简化了很多不必要的功能。1.3运行环境及开发环境1.3.1开发环境AMDAthlon(TM),512M内存,80G硬盘Microsoft®Windows™XPProfessionalMicrosoft®VisualStudio2003(CSharp)Microsoft®DeveloperNetworkforVisualStudio.NET20031.3.2运行环境Intel®Pentium®2及以上处理器,32M以上内存,4G以上硬盘Microsoft®Windows™9X/NT操作系统800*600或以上的屏幕分辨率确保机器上安装有.NetFrameWork1.0或者以上版本2.软件架构及系统用例图2.1系统架构软件的总体架构如图2.1:2.2系统总体用例用户新建账号发送邮件接收邮件纯文本邮件带附件邮件2.3程序功能框图客户端软件电子邮件接收系统电子邮件接收系统附加功能各种错误的提示简单邮件接收带有附件的邮件接收简单邮件发送带有附件的邮件发送电子邮件编写电子邮件的分类管理通讯簿管理3SMTP协议的研究由于要开发的是邮件客户端程序,就不得不用到SMTP协议和POP协议。而我个人负责的是邮件发送功能的实现,因此就必然会涉及到SMTP(SimpleMailTransferProtocol)协议。SMTP被用来在因特网上发送邮件,该协议规定了一些基本的命令和方法使客户端与服务器进行交互,以达到发送邮件的目的。3.1SMTP协议简介及工作原理3.1.1介绍简单邮件传输协议(SMTP)的目标是可靠高效地传送邮件,它独立于传送子系统而且仅要求一条可以保证传送数据单元顺序的通道。SMTP的一个重要特点是它能够在传送中接力传送邮件,传送服务提供了进程间通信环境(IPCE),此环境可以包括一个网络,几个网络或一个网络的子网。理解到传送系统(或IPCE)不是一对一的是很重要的。进程可能直接和其它进程通过已知的IPCE通信。邮件是一个应用程序或进程间通信。邮件可以通过连接在不同IPCE上的进程跨网络进行邮件传送。更特别的是,邮件可以通过不同网络上的主机接力式传送。3.2SMTP协议的命令和应答3.2.1SMTP协议的命令SMTP命令定义了邮件传输或由用户定义的系统功能。它的命令是由CRLF结束的字符串。而在带有参数的情况下,命令本身由SP和参数分开,如果未带参数可以直接和CRLF连接。邮箱的语法格式必须和接收站点的格式一致。下面讨论SMTP几个常用的命令和应答。下面是SMTP命令:HELOSPdomainCRLFMAILSPFROM:reverse-pathCRLFRCPTSPTO:forward-pathCRLFDATACRLFNOOPCRLFQUITCRLFTURNCRLF3.2.2SMTP的应答码对SMTP命令的响应是多样的,它确定了在邮件传输过程中请求和处理的同步,也保证了发送SMTP知道接收SMTP的状态。每个命令必须有且只有一个响应。SMTP响应由三位数字组成,其后跟一些文本。数字帮助决定下一个应该进入的状态,而文本对人是有意义的。三位的响应已经包括了足够的信息,不用再阅读文本,文本可以直接抛弃或者传递给用户。特别的是,文本是与接收和环境相关的,所以每次接收到的文本可能不同。在附录E中可以看到全部的响应码。正规的情况下,响应由下面序列构成:三位的数字,SP,一行文本和一个CRLF,或者也可以是一个多行响应。只有EXPN和HELP命令可以导致多行应答,然而,对所有命令,多行响应都是允许的。REPLYCODESBYFUNCTIONGROUPS500格式错误,命令不可识别(此错误也包括命令行过长)501参数格式错误502命令不可实现4命名控件MailSend由于在CSharp语言中,都是以命名控件来组织程序的。而所有的类都归属于一个特定的命名空间下。需要的命名空间系统本身自带了一部分,而且如果系统没有你需要的命名空间的话,就可以自己编写,本节中的这个命名空间就是由于需要而编写的。而调用某一个类中的某个变量成员的方法就是通过命名空间名.类名.变量成员来访问的,当然在CSharp中如果在程序开始通过Using命名空间名,就可以直接的象C++那样来访问成员变量,可以说相当的方便,这些都会在程序中体现出来,在此不再做过多的叙述。4.1发送邮件类SmtpMail4.1.1主要成员变量说明1)网络连接类及实例TcpClienttc为TCP网络服务提供客户端连接类TcpClient实例对象tc。TcpClient类提供了一些简单的方法,用于在同步阻塞模式下通过网络来连接、发送和接收流数据。而实例化的过程也是连接SMTP服务器的过程。它的重载方法之一的两个参数一个为服务器名称字符串,另一个为服务器的埠。2)提供用于网络访问的基础数据流及其实例NetworkStreamns此类提供访问网络的基础数据流的方法。其中最基本也是最重要的两个方法就是Write()和Read()方法,至于参数不再次赘述。3)一维字符串数组变量FilePath此字符串数组主要用来存放用户选择的附件的绝对路径名,并在发送带附件的邮件时用到。4)发送邮件所需的基本参数比如用于ESMTP等录检验用的用户名、密码,发送邮件需要的收信人,发信人地址以及主题等等在此不再赘述。4.1.2主要成员函数说明1)重载的构造函数SmtpMail()此函数主要用于在初始化过程中,把用户选择的附件的路径以参数的形式传给FilePath。2)添加附件的函数AddAttachment传给FilePath的路径,通过这样一个函数就可以循环的动态的添加到IList接口的一个对象中了,方便以后在具体的实现的过程中的使用。3)得到上传的附件的文件流GetStream由于在网络中的操作都是以网络流的形式来实现的,因此先将上传的附件转换成文件流,然后再用Write的方法把这些附件的文件流写入到网络中,来完成发送附件的操作。4)将字符串编码为Base64字符串的函数Base64Encode由于ESMTP的LOGIN认证机制是采用Base64编码,当用户发出AUTHLOGIN的命令后,服务器返回334的应答码等待用户输入。如果身份确认后服务器返回235的应答码,否则返回失败信息。所以要将用户名和密码转换成Base64编码然后再发给服务器。此函数的作用就是把给定的字符串转换成相应的Base64编码的字符串。5)发送SMTP命令的函数SendCommand这个函数的作用是把SMTP命令的字符串转换成对应的字节型值(C#中规定的Write方法只能写入字节型的数据)然后写入网络中,如果操作成功就返回一个标志为真的布尔型变量,如果操作失败或者发生异常就返回标志为假的布尔型变量6)接受服务器应答的函数RecvResponse它的作用就是从网络流中读取服务器返回的字节型的信息,将其转换成字符串型的变量,然后将其返回,可以通过其返回值来判断操作是否成功。具体实现代码如下所示:7)重载的函数Dialog它们的作用是与服务器交互,发送命令并接收回应。不同的是参数是字符串类型的那个函数,每次发送一条命令,并接受服务器的响应,根据响应的信息来判断交互的结果是否成功。而参数是字符串数组的函数每次发送的是一组命令,用于和服务器的交互,这个函数主要是用于ESMTP服务器的验证的功能,因为验证的过程是一个等待然后又输入的过程,因此将他们放在一个数组中有利于理解和操作。而他们的实现主要是通过调用上面的发送SMTP命令函数SendCommand以及接受SMTP服务器响应的函数RecvResponse来实现的。具体的代码如下所示:8)邮件发送程序SendMail这是整个程序的核心部分。具体的实现SMTP协议的程序正是通过它一步一步实现并最终实现发送简单邮件甚至带附件的邮件的功能。而它的实现是调用以上给出的各个函数的结果。以下就简单的通过几个SMTP命令的格式来实现privateboolSendEmail(){//连接网络try{//建立一个TCP连接tc=newTcpClient(mailserver,mailserverport);}catch{MessageBox.Show(连接失败,请确认);returnfalse;}//获取当前流的资料ns=tc.GetStream();SMTPCodeAdd();//验证网络连接是否正确if(RightCodeHT[RecvResponse().Substring(0,3)]==null){returnfalse;}string[]SendBuffer;if(ESmtp){SendBuffer=newString[4];SendBuffer[0]=EHLO+mailserver+enter;SendBuffer[1]=AUTHLOGIN+enter;SendBuffer[2]=Base64Encode(username)+enter;SendBuffer[3]=Base64Encode(password)+enter;if(!Dialog(SendBuffer,SMTP服务器验证失败,请核对用户名和密码。))returnfalse;}else{SendBufferstr=HELO+mailserver+enter;if(!Dialog(SendBufferstr,))returnfalse;}SendBufferstr=MAILFROM:+From++enter;if(!Dialog(SendBufferstr,发件人地址错误,或不能为空))returnfalse;//把传过来的收件人的地址分割然后提交给服务器stringsplit=;;string[]address=Regex.Split(Recipient,split);SendBuffer=newstring[address.Length];for(inti=0;iSendBuffer.Length;i++){SendBuffer[i]=RCPTTO:+address[i]++enter;}if(!Dialog(SendBuffer,收件人地址有误))returnfalse;SendBufferstr=DATA+enter;if(!Dialog(SendBufferstr,))returnfalse;SendBufferstr=From:+FromName++From++enter;SendBufferstr+=enter+.+enter;if(!Dialog(SendBufferstr,错误信件信息))returnfals