用C#.NET实现电子邮件客户程序
周华清 戴晟辉(东华理工学院计算机与通信系 江西 抚州 344000)
【摘要】通过C#这种VisualSTudio.NET中新引入的面向对象且类型安全的编程语言,在.NET平台上开发电子邮件客户程序。通过套接字编程实现网络通信连接,阐述SMTP(简单邮件传输协议)和POP3(邮局协议)的工作原理,然后具体讲解了根据SMTP协议开发电子邮件客户端的邮件发送程序,根据POP3协议开发电子邮件客户端的邮件接收程序。
【关键词】套接字 简单邮件传输协议 邮局协议
1 C#中套接字的编程
套接字是通信的基石,是支持TCP/IP协议网络通信的基本操作单元。可以将套接字看做不同主机间的进程进行双向通信的端点,它构成了单个主机内及整个网络间的编程界面。套接字存在于通信域中。通信域是为了处理一般的线程通过套接字通信而引进的一种抽象概念。套接字通常和同一个域中的套接字交换数据(数据交换也可能穿越域的界限,但这时一定要执行某种解释程序)。各种进程使用这个相同的域互相之间用Internet协议簇来进行通信。
针对C#中Socket编程用Socket类来进行,.NET 框架的Socket类是包含在System.Net.Sockets名字空间中的一个非常重要的类,其中为实现网络编程提供了大量的方法。使用Socket类开发windows网络应用程序原来有规可寻,它们在大多数情况下遵循大致相同的步骤。
在使用之前,你需要首先创建Socket对象的实例,这可以通过Socket类的构造方法来实现:
public Socket(AddressFamily addressFamily,SocketType socketType,ProtocolType protocolType);
其中,addressFamily参数指定Socket使用的寻址方案;socketType参数,指定Socket的类型;protocolType参数,指定Socket使用的协议。
下面的示例语句创建一个Socket,它可用于在基于TCP/IP的网络(如Internet)上通讯。
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
若要使用UDP而不是TCP,需要更改协议类型,如下面的示例所示:
Socket s=newSocket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
一旦创建Socket,在客户端,将可以通过Connect方法连接到指定的服务器,并通过Send/SendTo方法向远程服务器发送数据,而后可以通过Receive/ReceiveFrom方法从服务端接收数据;而在服务器端,你需要使用Bind方法绑定所指定的接口使Socket与一个本地终结点相联,并通过Listen方法侦听该接口上的请求,当侦听到用户端的连接时,调用Accept完成连接的操作,创建新的Socket以处理传入的连接请求。使用完Socket后,记住使用Shutdown方法禁用Socket,并使用Close方法关闭Socket。
2 SMTP和POP3协议的工作过程
在Internet上发送/接收电子邮件,要用到二个协议:SMTP(简单邮件传输协议)和POP3(邮局协议)。下面从SMTP和POP3协议入手,说明如何发送和接收电子邮件。
如图1,描述了从发件人使用主机(Client1)发送一封邮件从邮箱one@server1.com发往邮箱two@server2.com(其中域名为server1.com的邮件系统安装在Server1上,域名为server2.com的邮件系统安装在Server2上),收件人通过主机(Client2)接收到这封邮件的过程。在邮件传送的各个阶段要采用不同的网络协议。
(1)发件人采用Mail Client端软件在Client 1处撰写一封邮件发给收件人,首先是将该邮件从Client1发送到他本人的邮箱one@server1.com所在的服务器Server1上。这一步由Client1发往Server的过程采用的是SMTP协议。
图1 电子邮件传送过程示意图
(2)邮件可以从发件人的邮件服务器Server1直接传送到收件人邮箱two@server2.com所在的服务器Server2上,也可能需要经过第三方的邮件服务器Server3做中转后再送达接收方,这个过程称为Relay。这一步在Mail Server之间进行邮件传送的过程采用的是SMTP协议。邮件发送到接收方的服务器Server2上后,由Server2负责将该邮件投递到收件人的邮箱two@server2.com中,保存在Server2的磁盘阵列中。此时,邮件传送过程结束。
(3)收件人访问他在Server2上的邮箱two@server2.com时,采用Mail Client端软件Client2将Server2邮箱中的邮件下载到本地硬盘上阅读。这一步将邮件从Server下载到Client的过程采用的是POP3协议。
开发电子邮件客户端实际上也就是利用套接字(Socket)编程进行对话通讯,按照SMTP协议和POP3协议的规范完成邮件传输。
3 邮件发送模块的实现
SMTP协议是TCP/IP协议栈中的一个广为使用的上层协议。SMTP定义了如何在两个用户间传输邮件,使用了spooling的概念,允许将邮件从一个本地应用发送到一个SMTP应用。SMTP应用把邮件存储到一个设备或内存中,一旦邮件到达SMTP应用,该邮件被放入到一个队列中,一个服务器检查是否有邮件到达,然后试图投递到达的邮件。如果邮件的收方不存在,服务器然后还会再进行尝试。最终,如果邮件不能被投递,则将该邮件丢弃或返回给邮件的发送者。这一概念被称为端到端的投递,并且它将邮件保存在队列中,直到邮件被投递出去。我们可以从两个RFC中找到有关SMTP的讨论。RFC822描述了报文的结构,其中还包括了信封。RFC821规定了在两台机器间控制邮件交换的协议。
以下模块利用TcpClient类开发一个SMTP客户端,以发送邮件。
3.1 发送SMTP命令
SMTP服务器一般都识别UTF8码,所有发送命令都使用UTF8编码,每个命令均以回车换行符结束。下列代码实现命令的发送功能。
private void WriteToNetStream(ref NetworkStream NetStream.string Command)
{ string stringToSend = Command + "\r\n";
Byte[] arrayToSend = System.Text.Encoding.UTF8.GetBytes
(stringToSend.ToCharArray());
NetStream.Write(arrayToSend,0,arrayToSend.Length);}
3.2 答应码的接收
SMTP服务器对每一个都发出响应。下列代码实现接受答应码的功能。
private string ReadFromNetStream(ref NetworkStream NetStream)
{ byte[] bb=new byte[512];
NetStream.Read(bb,0,bb.Length);
string read=System.Text.Encoding.UTF8.GetString(bb);}
3.3 应答码检查
发送邮件客户端必须要检查应答码,以判断服务器是否已经执行了命令。如果服务器没有执行,则重新发送命令或采用其他措施。下列代码可以检查服务器是否正确地执行了命令。
private string checkForError(string strMessage,string check)
{ if (strMessage.Indexof(check) == -1)
{ return "err";}
else{return "correct";}}
上述方法的第一个参数是服务器返回信息,第二个参数是要检查的答应码。如果服务器返回的信息中不存在第二个参数的字符串,则返回“err”,表明服务器没正确执行命令。
3.4 发送邮件
客户端发出DATA命令,在服务器作出354应答后,即可开始邮件内容的发送。邮件以<CRLF>.<CRLF>结束。下列代码实现了邮件发送功能。
private void sendMail(ref NetworkStream NetStream, string message)
{ Byte[] attayToSend = System.Text.Encoding.UTF8.GetBytes
(message.ToCharArray());
NetStream.Write(arrayToSend,0,arrayToSend.Length);}
4 邮件接收模块的实现
POP允许本地邮件UA(User Agent,用户代理)连接SERVER并将邮件取回到用户本地系统,用户也在本地机上阅读和响应消息。POP3UA通过TCP/IP与服务器连接(通常使用端口110)。输入用户名和口令经过认证后,UA可通过POP3命令取回或删除邮件。POP3仅仅是接收协议。POP3UA使用SMTP向服务器发送邮件。
利用POP3协议开发一个邮件接收程序,使用USER、PASS、STAT、LIST、RETR、DELE、QUIT命令。下面开始开发过程。
4.1 发送命令码
在此发送命令码也使用的是ASCII码。下列方法用于向服务器发送命令码:
private void WriteToNetStream(ref NetworkStream NetStream.string Command)
{ string stringToSend = Command + "\r\n";
Byte[]arrayToSend= System.Text.Encoding.ASCII.GetBytes
(stringToSend.ToCharArray());
NetStream.Write(arrayToSend,0,arrayToSend.Length);}
4.2 接收服务器应答
一般而言,接收服务器应答,既可使用ASCII码,也可以使用UTF8码,这里使用的是ASCII码。下列方法用于接受服务器的应答。
private string ReadFromNetStream(ref NetworkStream NetStream)
{ byte[] bb=new byte[1024];
NetStream.Read(bb,0,bb.Length);
string read=System.Text.Encoding.ASCII.GetString(bb);
return read;}
4.3 接受邮件
在此接收邮件用UTF8码,当遇到<CRKF>.<CRLF>,则结束读取数据。下列代码用于接收邮件:
private void ReadMail(ref NetworkStream NetStream,int number)
{ int k=0;
bool check=false;
byte[] bb=new byte[6400];
while(!check)
{ k=NetStream.Read(bb,0,bb.Length);
string read=System.Text.Encoding.UTF8.GetString(bb,0,k);
int x= read.IndexOf("\r\n.\r\n");
if(x!=-1)
{ check=true;}
richTextBox2.AppendText(read);
WriteToNetStream(ref
NetStream,"DELE "+number.ToString());
string back=ReadFromNetStream(ref NetStream );
richTextBox1.AppendText("DELE "+number.ToString()
+"命令应答:"+back+"\r\n"); }}
5 结束语
把邮件发送模块和邮件接收模块合起来,就成为一个电子邮件的客户端程序。在实际应用中,在上面基础上加以改进,如果再进一步结合数据库技术,这可以开发出易用、可靠的电子邮件的客户端程序。
(收稿日期:2004-02-25;电子邮件:zhouhuaqing@peoplemail.com.cn)
Programming e-mail client with c#.net
Abstract: Develop a program for e-mail client on the flat of .net with C#. C# is a new language of visual studio .net , is modern, object-oriented and is safe in type. Realize the network correspondence depending Socket programming. Expatiate the principle of SMTP(Simple Mail Transfer Protocol) and POP3(Post office Protocol), then explain developing a program to send e-mail according to the SMTP agreement development E-mail mail customer and carry to receive the procedure developing a program to receive e-mail according to the POP3 in detail.
Key Words: Socket; Simple Mail Transfer Protocol; Post Office Protocol
参 考 文 献
[1]谢希仁. 计算机网络. 第2版. 北京:清华大学出版社,1999
[2]Charles Petzold. Windows程序设计. 第5版. 北京:北京大学出版社,1999
[3]Anthony Jones 和 Jim Ohlund. Windows网络编程. 第2版. 北京:清华大学出版社,2002class="Apple-converted-space">