# [C# 网络编程系列]专题十:实现简单的邮件收发器
引言:
在我们的平常工作中,邮件的发送和接收应该是我们经常要使用到的功能的。因此知道电子邮件的应用程序的原理也是非常有必要的,在这一个专题中将介绍电子邮件应用程序的原理、电子邮件应用程序中涉及的协议和实现一个简答的电子邮件收发器程序。
**一、邮件应用程序基本知识**
**1.1 电子邮件原理及相关协议**
说到电子邮件的原理,其实和我们现实生活中寄邮件和寄包裹是一样的原理的。就让我们先回顾下现实生活中寄邮件的流程吧——首先,我们先写好信,信封上面写好收信人的地址,写信人的地址,然后把信放到寄信箱中,然后邮局的人会某个时候去这个信箱中的信取出来,然后邮局的人根据信封上写的收信人地址进行转发到当地的邮局,当地邮局然后把信寄到收信人的信箱中(_寄包裹的话可能会电话联系,像我们在淘宝,京东买的东西的,收货人就是通过电话联系一样_),最后收信人会到自己的信箱中收取信件。上面大致是我们平时生活中寄信的一个流程的。前面已经讲过电子邮件的原理和这个差不多的,下面就介绍了本专题中电子邮件的原理,大家可以和现实生活中的寄信过程进行对比下的,这样可以更加容易理解和掌握:
我们通过电子邮件应用(_例如 基于客户端的Outlook电子邮件软件 和一些基于Web的电子邮件系统——新浪邮箱、谷歌邮箱、QQ邮箱等都属于电子邮件应用_)将一封写好的邮件(_相当于现实生活中的信,当然邮件也要写明收件人地址,邮件内容等信息的_)通过电子邮件协议(_SMTP,在后面的电子邮件相关协议中会介绍_)发送到SMTP服务器(_就是存储邮件的地方,相当于生活中的邮局一样_),然后SMTP服务器根据收件人的地址通过SMTP协议转发到相应SMTP接收服务器上,(_SMTP服务器进行转发相当于现实生活中邮局的人配送信的过程,配送到收件人当地的邮局,然而现实生活中邮局都是一家,所以可以相互识别——意思就是发送到当地邮局,当地邮局会接收,并且帮助你发送到指定人的信箱中,在网上上就是通过SMTP协议来规定这样的一个过程的,发送到别人的SMTP服务器上别人的服务器必须要认识发送来的邮件并接收_)结束,接收端邮件服务器(POP3服务器)把邮件存放到接受者的电子信箱内(相当于当地邮局的人把信放到收信人的邮箱中),最后收件人可以登录自己的电子信箱,再与POP3服务器进行连接,从POP3服务器上下载发送来的邮件,这样在收件人的电子信箱中就可以看到发送来的电子邮件了(_这就是现实生活中收信人从自己的信箱中取信的一个过程_)。
_注:括号中都是个人的理解,如果有什么不对的地方还望大家指出来,我好及时更正。_
上面已经把电子邮件的原理和现实生活中寄信的过程进行对比,相信大家可以更加清楚电子邮件的原理和发送接收过程的,其实网络上的很多应用都可以以现实生活的例子去理解,这样的话我认为可以加深对知识的理解。下面就介绍下电子邮件中的相关协议的内容:
网络上的应用的核心就是协议,因为协议让网络上的客户端相互认识发生来的数据,所以电子邮件应用也不例外,也有相关的电子邮件协议来完成发送电子邮件和接收电子邮件的过程,这些协议主要是:SMTP(简单邮件传输协议,Simple Mail Transfer Protocol)、POP3(邮局协议,Post Office Protocol)和IMAP(网络邮件访问协议,Internet Message Access Protocol)。
* SMTP——SMTP 主要负责将邮件从一台机器转发至另一台机器(可以对照上面电子邮件的过程来理解SMTP的作用)
* POP3——3表示POP协议的版本,主要负责将邮件从邮箱中(POP3服务器)传输到本地计算机。
* IMAP——现在常用的版本为第四版本,即IMAP4,主要负责邮件的检索和处理功能,客户端不需要下载邮件到本地计算机,可直接从邮件客户端软件对服务器上的信件和文 件目录进行操作,它是POP3的替代协议的。
**1.2 邮件系统的分类**
邮件系统主要分为两类的——基于客户端的邮件系统和基于Web浏览器的邮件系统。Office OutLook就是基于客户端的邮件客户端系统,而像我们经常使用的QQ邮箱、新浪、网易邮箱等都是属于基于Web浏览器的邮件系统,基于客户端的邮件系统的收发过程,通过下面的图片来描述(图片从网上摘下的):
![](https://box.kancloud.cn/2016-01-23_56a2eb37d7b1f.gif)
图 1.1 基于客户端的邮件收发过程
发送方通过邮件客户端,将编辑好的邮件向邮件服务(SMTP服务器,在发送过程中也叫发送端邮件服务器)发送,发送端邮件服务器根据收件人的地址来识别接收端邮件服务器(POP3服务器),然后向POP3服务器发送邮件信息,接收端邮件服务器将邮件存放在接收者的电子信箱中,并告知接收者有新邮件,接收者通过邮件客户端与POP3服务器连接后,就可以查看新邮件。
然而,基于Web浏览器的邮件系统与基于客户端的邮件系统不同的地方有:
* 基于Web浏览器邮件系统用户代理(代理的概念也就是用户不是直接与服务器进行通信,而是通过代理的方式,让代理去与服务器通信,然后用户在从代理中获的服务器的信息,代理也就是中间人的作用,相当于生活中中介,在.net中很多技术都用到了代理,例如委托的概念其实也就是代理的一个概念的)是Web浏览器,基于客户端的邮件系统而是邮件客户端应用程序,一般是Windows Form程序。
* 浏览器发送邮件到SMTP服务器和从POP3服务器中获得邮件的方式都是通过HTTP协议来实现,与基于客户端的邮件系统不同(基于客户端的邮件系统发送通过SMTP协议或ESMTP(Extended SMTP),获得通过POP3或IMAP协议)。
**1.3 目前主要的电子邮件服务系统**
电子邮件服务系统——就是向大家提供邮箱服务的服务系统,这样的系统当然是由专门的公司进行研发的,我们一般叫这样的公司为邮件服务商,我们平常使用的网易邮箱,新、Gmail邮箱等都是建立在电子邮件服务系统(_这里我的理解是——我们使用的新浪,网易等邮箱相当于现实生活中每个人的信箱,通过信箱可以获得邮局来的信,同样道理通过邮箱可以获得邮件服务系统的邮件,这样电子邮件系统相当于邮局_) 。现在主要电子邮件服务系统主要有下面几种:
* 基于Postfix/Qmail的邮件系统。例如,雅虎邮箱基于Qmail系统
* 微软Exchange 邮件系统
* IBM Lotus Domino邮件系统
* Scalix邮件系统
* Zimbra邮件系统
* MDeamon邮件系统
**二、.Net 平台对邮件发送功能的支持**
在.NET类库中,在System.Net.Mail命名空间下定义了对邮件处理的类,这样使邮件的发送更加方便(_这些类也就是对SMTP协议的封装,使我们更好地区编程,只需要使用类中的方法和属性等去完成邮件的发送,避免写复杂的SMTP协议的命令_),下面是一张在System.Net.Mail命名空间下对邮件发送的支持的类截图:
![](https://box.kancloud.cn/2016-01-23_56a2eb37ec350.png)
从图片中类的名字中也可以看出每个类的作用的,在这里我就不一个介绍的, 大家可以参考MSDN去看每个类的使用,并且我在后面程序的实现部分也会有详细的注释去介绍程序中使用到类的使用。从图中还可以i看出一点——就是只有SMTP的字样,却没有POP3这样的字样的,这说明.Net类库本身中并没有提供对POP3协议的封装类,但是我们可以使用Jmail组件来完成从POP3服务器中收取邮件的功能,具体的使用将在后面的邮件收发器程序中邮件的接收部分介绍的。
**三、邮件收发器程序的实现**
**3.1 邮件发送功能的实现**
**3.1.1 SMTP协议**
SMTP 协议是用于电子邮件的传输的协议,电子邮件是通过SMTP服务器进行发送的,SMTP服务器的默认端口为25,通常发送邮件有两种方式——一种是不使用客户端认证,即客户端可以使用匿名发送邮件(这种方式叫做SMTP);另一种是客户端必须提供用户名和密码认证(这种方式叫做ESMTP,Extended SMTP)目前大部分邮件服务器采用用户名和密码认证的方式。
客户端发送邮件过程为——先通过客户端软件(本程序中的邮件收发器)将邮件发送到SMTP服务器,然后再由SMTP服务器发送到目标SMTP服务器。下面介绍SMTP协议的内容:
SMTP协议总共定义了14个命令,命令由命令码和气候的参数域组成, 不区别大小写的(通过前面专题的讲述可以得出各个协议的命令组成都差不多的),下面就简单介绍下5个常用的命令码
|名称 |解释 |
| --- | --- |
|HELO或EHLO |发送连接到服务器的命令,EHLO主要用于与ESMTP服务器建立连接时发送的命令 |
|MAIL FROM |指定发件人的邮件地址 |
|Rcpt to |指定收件人的邮件地址 |
|Data |指定邮件正文内容,邮件内容以单独一行 ”.” 表示接触 |
|Quit |关闭与服务器的连接,然后退出 |
电子邮件由信封、首部、正文和结束符号4部分组成,下面就具体介绍下这4个部分的内容:
1\. 信封
信封包括发信人的邮件地址和接收人的邮件地址,具体对应两条SMTP命令——Mail from: [mytest1989@sina.cn](mailto:mytest1989@sina.cn)(发信人的地址)和Rcpt to: [794170314@qq.com](mailto:794170314@qq.com)
2\. 首部
首部中常用的命令有:
* Subject:<邮件主题>——表示邮件的主题
* Date:<时间>——表示发邮件的时间
* reply-to:<邮件地址>——表示邮件的回复地址
* Content-Type:<邮件类型>——表示邮件包含文本、HTML超文本和附件的类型。
* X-Priority:<邮件优先级>——表示邮件发送的优先级,优先级为3表示为普通邮件;如 X-Priority:3
3\. 正文
正文当然指的就是邮件的内容了, 用Data命令指定,首部以一个空行结束,下面就是正文部分
4\. 结束符号
邮件以“."结束,
接收方收到SMTP命令之后,会给出一个响应码,每个命令都只有一个响应码,SMTP响应码也是由3位数字组成,后面附加一些文本信息,响应信息的格式为:
响应码<空格>文本信息<回车换行>
客户端发出一条命令后,服务器端返回一个响应,发送者在发送下一条命令前必须等待服务器的响应,成功接收到响应码后才继续发送命令。
附:SMTP常用的响应码:
|响应码 |解释 |响应码 |解释 |
| --- | --- | --- | --- |
|211 |系统状态或系统帮助响应 |421 |服务未就绪,关闭了传输通道 |
|214 |帮助信息 |501 |参数格式错误 |
|220 |服务就绪 |502 |命令不可实现 |
|221 |服务关闭传输通道 |535 |用户验证失败 |
|235 |用户验证成功 |553 |邮箱名不可用,要求的操作未执行 |
|334 |等待用户输入验证 |554 |操作失败 |
|354 |开始邮件输入 |
**3.1.2 邮件的发送过程**
第一步:客户端与服务器建立连接(该步中客户端首先发送EHLO local 连接命令,服务器如果返回“220”响应码表示服务器准备就绪了,客户端再继续发送“Auto login”命令,请求登录,服务器收到命令后返回“334”响应码,表示要输入用户名,之后客户端发送用户名命令,等到响应后再发送密码命令,具体在程序的实现中也会有注释。)
第二步:客户端发送邮件的信封
第三步:开始发送邮件数据,(包括邮件首部,正文和结束符号,注:结束符号要单独占一行,表示邮件发送结束)
第四步: 客户端与服务器断开连接。
**3.1.3 发送功能的实现代码**
相信有了上面的理论解释邮件发送的过程后,实现邮件发送的功能并不难的,并且.net类库中SMTPClient类帮我们封装了SMTP协议,使得我们实现邮件发送功能就不要记住那些具体的命令了, 只需要使用该类中提供的方法来完成邮件的发送(当然你也可以通过发送命令的方式实现,SMTPClient类的方法也是帮我们完成发送命令功能而已的),下面是邮件发送功能的核心代码:
View Code
```
1 #region 邮件发送功能代码
2 // 添加附件
3 private void btnAddFile_Click(object sender, EventArgs e)
4 {
5 OpenFileDialog openFileDialog = new OpenFileDialog();
6 openFileDialog.CheckFileExists = true;
7 // 只接受有效的文件名
8 openFileDialog.ValidateNames = true;
9 // 允许一次选择多个文件作为附件
10 openFileDialog.Multiselect = true;
11 openFileDialog.Filter = "所有文件(*.*)|*.*";
12 if (openFileDialog.ShowDialog() != DialogResult.OK)
13 {
14 return;
15 }
16 if (openFileDialog.FileNames.Length > 0)
17 {
18 // 因为这里允许选择多个文件,所以这里用AddRange而没有用Add方法
19 cmbAttachment.Items.AddRange(openFileDialog.FileNames);
20 }
21 }
22
23 // 删除附件
24 private void btnDeleteFile_Click(object sender, EventArgs e)
25 {
26 int index = cmbAttachment.SelectedIndex;
27 if (index == -1)
28 {
29 MessageBox.Show("请选择要删除的附件!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
30 return;
31 }
32 else
33 {
34 cmbAttachment.Items.RemoveAt(index);
35 }
36 }
37
38 // 发送邮件
39 private void btnSend_Click(object sender, EventArgs e)
40 {
41 this.Cursor = Cursors.WaitCursor;
42 // 实例化一个发送的邮件
43 // 相当于与现实生活中先写信,程序中把信(邮件)抽象为邮件类了
44 MailMessage mailMessage = new MailMessage();
45 // 指明邮件发送的地址,主题,内容等信息
46 // 发信人的地址为登录收发器的地址,这个收发器相当于我们平时Web版的邮箱或者是OutLook中配置的邮箱
47 mailMessage.From = new MailAddress(tbxUserMail.Text);
48 mailMessage.To.Add(txbSendTo.Text);
49 mailMessage.Subject = txbSubject.Text;
50 mailMessage.SubjectEncoding = Encoding.Default;
51 mailMessage.Body = richtbxBody.Text;
52 mailMessage.BodyEncoding = Encoding.Default;
53 // 设置邮件正文不是Html格式的内容
54 mailMessage.IsBodyHtml = false;
55 // 设置邮件的优先级为普通优先级
56 mailMessage.Priority = MailPriority.Normal;
57 //mailMessage.ReplyTo = new MailAddress(tbxUserMail.Text);
58
59 // 封装发送的附件
60 System.Net.Mail.Attachment attachment = null;
61 if (cmbAttachment.Items.Count > 0)
62 {
63 for (int i = 0; i < cmbAttachment.Items.Count; i++)
64 {
65 string fileNamePath = cmbAttachment.Items[i].ToString();
66 string extName = Path.GetExtension(fileNamePath).ToLower();
67 if (extName == ".rar" || extName == ".zip")
68 {
69 attachment = new System.Net.Mail.Attachment(fileNamePath, MediaTypeNames.Application.Zip);
70 }
71 else
72 {
73 attachment = new System.Net.Mail.Attachment(fileNamePath,MediaTypeNames.Application.Octet);
74 }
75
76 // 表示MIMEContent-Disposition标头信息
77 // 对于ContentDisposition具体类的解释大家可以参考MSDN
78 // 这里我就不重复贴出来了,给个地址: http://msdn.microsoft.com/zh-cn/library/System.Net.Mime.ContentDisposition.aspx (着重看备注部分)
79 ContentDisposition cd = attachment.ContentDisposition;
80 cd.CreationDate = File.GetCreationTime(fileNamePath);
81 cd.ModificationDate = File.GetLastWriteTime(fileNamePath);
82 cd.ReadDate = File.GetLastAccessTime(fileNamePath);
83 // 把附件对象加入到邮件附件集合中
84 mailMessage.Attachments.Add(attachment);
85 }
86 }
87
88 // 发送写好的邮件
89 try
90 {
91 // SmtpClient类用于将邮件发送到SMTP服务器
92 // 该类封装了SMTP协议的实现,
93 // 通过该类可以简化发送邮件的过程,只需要调用该类的Send方法就可以发送邮件到SMTP服务器了。
94 smtpClient.Send(mailMessage);
95 MessageBox.Show("邮件发送成功!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
96
97 }
98 catch(SmtpException smtpError)
99 {
100 MessageBox.Show("邮件发送失败:[" + smtpError.StatusCode + "];["
101 + smtpError.Message+"];\r\n["+smtpError.StackTrace+"]."
102 ,"错误",MessageBoxButtons.RetryCancel,MessageBoxIcon.Error);
103 }
104 finally
105 {
106 mailMessage.Dispose();
107 this.Cursor = Cursors.Default;
108 }
109 }
110
111 #endregion
```
**3.2 邮件接收功能的实现**
**3.2.1 POP3协议**
前面介绍了邮件的发送,当然接收者需要登录邮箱来查看收到的邮件了,此时就必有有一个协议去读取服务器上邮件,POP3就是这样的一个协议。还有两外一种协议也是用来接收邮件的——IMAP协议,它与POP3协议区别有:1\. IMAP使用的端口号是143而POP3邮件服务器通过监听110端口来提供POP3服务;
2 . IMAP 允许客户端在邮件服务器上建立文件夹来保持邮件,而不用把邮件下载到本地。而POP3需要把邮件下载到本地。
和SMTP协议一样,客户端要通过POP3协议从POP3服务器上获取邮件,也需要先与POP3服务器建立TCP连接,等待服务器向客户端发送确认信息表明连接成功时,客户端才可以继续发送命令给服务器来获取邮件,在POP3协议中,规定的命令也是几十条的,每条命令由命令和参数两部分组成,都是以回车换行结束,并且命令和参数之间由空格分隔,命令通常也是由3-4个字母组成,参数最多可以为40个字符的长度,而服务器的响应信息是由一个状态码和**可能**附加信息的字符组成,所有的响应信息也是以回车换行结束的。状态码和其他协议定义的状态码有点不一样,POP3服务器响应的状态码有两种——“+OK”(确定)和"-ERR"(失败)。这样客户端可以通过检查响应的状态码所包含的字符来判断服务器是否响应客户端发送的命令,即响应信息中包含“+OK”表示成功响应,包含“-ERR”表示服务器未响应。同时在程序的实现中大家可以通过Debug来查看响应消息的组成,这样可以加深理解。
**3.2.2 邮件接收的过程**
客户端从服务器接收邮件的过程主要经历3个状态:授权状态、操作状态和更新状态
(1)授权状态——客户端发送与POP3服务器的TCP连接请求,服务器接收后发送一个响应确认信息之后,此时客户端需要发送正确的用户名和密码进行确认,因为在邮件服务器上有很多用户邮箱,只有提供正确的用户名和密码才有权限访问自己的邮箱,就像现实生活中我们邮箱的钥匙一样的。
发送用户名命令: USER mytest1989@sina.cn
发送密码命令: PASS ******(这两个命令都在代码中有给出的,大家可以参考代码来理解邮件的接收过程)
(2) 操作状态——如果客户端提供了正确的用户名和密码,则授权状态也就通过了,就相当于打开了在服务器上自己的邮箱,现在用户就有权限进去下载,查看和删除邮件等操作的,然后在现实生活中的取邮件和删除邮件都很简单(只要打开了邮箱门,用手去拿就可以了),然后在网络应用上,这些操作都需要发送POP3命令给服务器,服务器接收到命令后再给出响应。操作中常用的命令有:
* STAT 命令——该命令从服务器中获取邮件总数和总字节数,服务器响应命令返回邮件总数和总字节数
如:
```
客户端发送POP3命令: STAT
服务器响应命令: +OK 2 1340
服务器响应命令:
```
* LIST 命令——该命令从服务器中获得邮件列表和大小,服务器响应命令返回列出邮件列表和大小。
如:
```
客户端发送POP3命令:LIST
服务器响应命令: +OK 2 message(1430 octect)
服务器响应命令:1 700
服务器响应命令:2 730
服务器响应命令:<一个空行>
```
* RETR 命令—— 该命令从服务器中获得一个邮件,格式为 RETR <邮件编号>
如:
```
客户端发送POP3命令:RETR 1
服务器响应命令: 700 octets
服务器响应命令:<邮件头和内容>
服务器响应命令: <空行>
```
* DELETE 命令——该命令告诉服务器将邮件标记为删除。(此时只是逻辑删除)
(3)更新状态——客户端发送QUIT命令后,此时就进入更新状态,POP3服务器释放在操作状态中取得的资源,并将逻辑删除的邮件进行物理删除,然后关闭与客户端的TCP连接。这样整个邮件处理的过程就结束了。
**3.2.3 接收功能的实现代码**
有了前面接收邮件过程的介绍,再参考代码的实现,相信大家可以更好的理解客户端从POP3服务器中获取邮件的过程的,由于.net类库并没有帮我们封装POP3协议的实现类,要实现邮件的获取可以采用发送命令的方式,也可以使用Jmail组件,这个组件其实就是POP3协议的封装类,既然微软没有帮我们做,其他公司帮我们做好后来帮助我们简单的实现邮件的接收的一个类库罢了。然后在使用这个组件的过程中出现了好几个问题的,在源码中我都解释,大家可以下载源代码后查看的。
实现邮件接收的核心代码如下:
View Code
```
// 登录邮箱(这里是本程序——邮件收发器)
private void btnLogin_Click_1(object sender, EventArgs e)
{
// 与POP3服务器建立TCP连接
// 建立连接后把服务器上的邮件下载到本地
// 设置当前界面的光标为等待光标(就是我们看到的一个动的圆形)
Cursor.Current = Cursors.WaitCursor;
lsttbxStatus.Items.Clear();
try
{
// POP3服务器通过监听TCP110端口来提供POP3服务的
// 向POP3服务器发出tcp请求
tcpClient = new TcpClient(tbxPOP3Server.Text, 110);
lsttbxStatus.Items.Add("正在连接...");
}
catch
{
MessageBox.Show("连接失败", "错误", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error);
lsttbxStatus.Items.Add("连接失败!");
return;
}
// 连接成功的情况
networkStream = tcpClient.GetStream();
streamReader = new StreamReader(networkStream, Encoding.Default);
streamWriter = new StreamWriter(networkStream, Encoding.Default);
streamWriter.AutoFlush = true;
string str;
// 读取服务器返回的响应连接信息
str = GetResponse();
if (CheckResponse(str) == false)
{
lsttbxStatus.Items.Add("服务器拒接了连接请求");
return;
}
// 如果服务器接收请求
// 向服务器发送凭证——用户名和密码
// 向服务器发送用户名,请求确认
lsttbxStatus.Items.Add("核实用户名阶段...");
SendToServer("USER " + tbxUserMail.Text);
str = GetResponse();
if (CheckResponse(str) == false)
{
lsttbxStatus.Items.Add("用户名错误.");
return;
}
// 用户名审核通过后再发送密码等待确认
// 向服务器发送密码,请求确认
SendToServer("PASS "+txbPassword.Text);
str = GetResponse();
if (CheckResponse(str) == false)
{
lsttbxStatus.Items.Add("密码错误!");
return;
}
lsttbxStatus.Items.Add("身份验证成功,可以开始会话");
// 向服务器发送LIST 命令,请求获得邮件列表和大小
lsttbxStatus.Items.Add("获取邮件....");
SendToServer("LIST");
str = GetResponse();
if (CheckResponse(str) == false)
{
lsttbxStatus.Items.Add("获取邮件列表失败");
return;
}
lsttbxStatus.Items.Add("邮件获取成功");
// 窗口控件控制
tabControlMyMailbox.Enabled = true;
btnReadMail.Enabled = false;
btnDownLoad.Enabled = false;
btnDeleteMail.Enabled = false;
// 登陆成功后实例化邮件发送对象,以便后面完成发送邮件的操作
// 实例化邮件发送类(SmtpClient)对象
if (smtpClient == null)
{
smtpClient = new SmtpClient();
smtpClient.Host = tbxSmtpServer.Text;
smtpClient.Port = 25;
// 不使用默认凭证,即需要认证登陆
smtpClient.UseDefaultCredentials = false;
smtpClient.Credentials = new NetworkCredential(tbxUserMail.Text, txbPassword.Text);
smtpClient.DeliveryMethod = SmtpDeliveryMethod.Network;
}
// 登陆成功后,自动接收新邮件
// 开始接收邮件
try
{
btnRefreshMailList.PerformClick();
}
catch
{
MessageBox.Show("读取邮件列表失败!", "错误", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error);
}
lsttbxStatus.Items.Add("登陆成功!");
lsttbxStatus.TopIndex = lsttbxStatus.Items.Count - 1;
Cursor.Current = Cursors.Default;
// 窗口控件控制
richtbxMailContentReview.Enabled = true;
tbxUserMail.Enabled = false;
txbPassword.Enabled = false;
btnLogin.Enabled = false;
btnLogout.Enabled = true;
tbxSmtpServer.Enabled = false;
tbxPOP3Server.Enabled = false;
btnReadMail.Enabled = true;
btnDownLoad.Enabled = true;
btnDeleteMail.Enabled = true;
tabControlMyMailbox.Focus();
}
#region 处理与POP3服务器交互事件
// 获取服务器响应的信息
private string GetResponse()
{
string str = null;
try
{
str = streamReader.ReadLine();
if (str == null)
{
lsttbxStatus.Items.Add("连接失败——服务器没有响应");
}
else
{
lsttbxStatus.Items.Add("收到:[" + str + "]");
if (str.StartsWith("-ERR"))
{
str = null;
}
}
}
catch(Exception err)
{
lsttbxStatus.Items.Add("连接失败:[" + err.Message + "]");
}
return str;
}
// 检查响应信息
private bool CheckResponse(string responseString)
{
if (responseString == null)
{
return false;
}
else
{
if (responseString.StartsWith("+OK"))
{
return true;
}
else
{
return false;
}
}
}
// 向服务器发送命令
private bool SendToServer(string str)
{
try
{
// 这里必须使用WriteLine方法的,因为POP3协议中定义的命令是以回车换行结束的
// 如果客户端发送的命令没有以回车换行结束,POP3服务器就不能识别,也就不能响应客户端的请求了
// 如果想用Write方法,则str输入的参数字符中必须包含“\r\n”,也就是回车换行字符串。
streamWriter.WriteLine(str);
streamWriter.Flush();
lsttbxStatus.Items.Add("发送:[" + str + "]");
return true;
}
catch(Exception ex)
{
lsttbxStatus.Items.Add("发送失败:[" + ex.Message + "]");
return false;
}
}
#endregion
```
**3.3 程序运行结果演示**
首先输入邮箱名和密码登录到POP3服务器来获取邮件列表的演示:
![](https://box.kancloud.cn/2016-01-23_56a2eb38099bf.jpg)
然后在邮件列表中选中一个邮件进行阅读,然后进行回复邮件的操作演示(邮件的发送都可以附加附件发送出去):
阅读邮件的界面:
![](https://box.kancloud.cn/2016-01-23_56a2eb382339c.png)
回复邮件的界面:
![](https://box.kancloud.cn/2016-01-23_56a2eb383e2c6.png)
同时点击发送按钮后,就可以把邮件发送到sina的SMTP服务器上,再由新浪的SMTP服务器转发到QQ的SMTP服务器,QQ的POP3服务器中QQ的SMTP服务器获取收到的邮件,当QQ用户输入正确的邮箱名和密码后就可以从QQ的POP3服务器上获取收到的邮件。
点击发送按钮后成功发送邮件的图片:
![](https://box.kancloud.cn/2016-01-23_56a2eb385885d.png)
**四、总结**
介绍到这里,本专题的内容就已经介绍完了,希望通过本专题可以让大家明白邮件发送和接收的原理,并且可以自定义一个简单邮件收发器的功能的,在后面一专题将介绍FTP协议(文件传输协议),并实现一个简单的文件上传和下载的程序。
源代码下载地址: [http://files.cnblogs.com/zhili/MailSendAndReceive.zip](http://files.cnblogs.com/zhili/MailSendAndReceive.zip) ,如果觉得有帮助的话,还望大家推荐下,谢谢大家的支持
- C# 基础知识系列
- C# 基础知识系列 专题一:深入解析委托——C#中为什么要引入委托
- C# 基础知识系列 专题二:委托的本质论
- C# 基础知识系列 专题三:如何用委托包装多个方法——委托链
- C# 基础知识系列 专题四:事件揭秘
- C# 基础知识系列 专题五:当点击按钮时触发Click事件背后发生的事情
- C# 基础知识系列 专题六:泛型基础篇——为什么引入泛型
- C# 基础知识系列 专题七: 泛型深入理解(一)
- C# 基础知识系列 专题八: 深入理解泛型(二)
- C# 基础知识系列 专题九: 深入理解泛型可变性
- C#基础知识系列 专题十:全面解析可空类型
- C# 基础知识系列 专题十一:匿名方法解析
- C#基础知识系列 专题十二:迭代器
- C#基础知识 专题十三:全面解析对象集合初始化器、匿名类型和隐式类型
- C# 基础知识系列 专题十四:深入理解Lambda表达式
- C# 基础知识系列 专题十五:全面解析扩展方法
- C# 基础知识系列 专题十六:Linq介绍
- C#基础知识系列 专题十七:深入理解动态类型
- 你必须知道的异步编程 C# 5.0 新特性——Async和Await使异步编程更简单
- 全面解析C#中参数传递
- C#基础知识系列 全面解析C#中静态与非静态
- C# 基础知识系列 C#中易混淆的知识点
- C#进阶系列
- C#进阶系列 专题一:深入解析深拷贝和浅拷贝
- C#进阶系列 专题二:你知道Dictionary查找速度为什么快吗?
- C# 开发技巧系列
- C# 开发技巧系列 使用C#操作Word和Excel程序
- C# 开发技巧系列 使用C#操作幻灯片
- C# 开发技巧系列 如何动态设置屏幕分辨率
- C# 开发技巧系列 C#如何实现图片查看器
- C# 开发技巧 如何防止程序多次运行
- C# 开发技巧 实现属于自己的截图工具
- C# 开发技巧 如何使不符合要求的元素等于离它最近的一个元素
- C# 线程处理系列
- C# 线程处理系列 专题一:线程基础
- C# 线程处理系列 专题二:线程池中的工作者线程
- C# 线程处理系列 专题三:线程池中的I/O线程
- C# 线程处理系列 专题四:线程同步
- C# 线程处理系列 专题五:线程同步——事件构造
- C# 线程处理系列 专题六:线程同步——信号量和互斥体
- C# 多线程处理系列专题七——对多线程的补充
- C#网络编程系列
- C# 网络编程系列 专题一:网络协议简介
- C# 网络编程系列 专题二:HTTP协议详解
- C# 网络编程系列 专题三:自定义Web服务器
- C# 网络编程系列 专题四:自定义Web浏览器
- C# 网络编程系列 专题五:TCP编程
- C# 网络编程系列 专题六:UDP编程
- C# 网络编程系列 专题七:UDP编程补充——UDP广播程序的实现
- C# 网络编程系列 专题八:P2P编程
- C# 网络编程系列 专题九:实现类似QQ的即时通信程序
- C# 网络编程系列 专题十:实现简单的邮件收发器
- C# 网络编程系列 专题十一:实现一个基于FTP协议的程序——文件上传下载器
- C# 网络编程系列 专题十二:实现一个简单的FTP服务器
- C# 互操作性入门系列
- C# 互操作性入门系列(一):C#中互操作性介绍
- C# 互操作性入门系列(二):使用平台调用调用Win32 函数
- C# 互操作性入门系列(三):平台调用中的数据封送处理
- C# 互操作性入门系列(四):在C# 中调用COM组件
- CLR
- 谈谈: String 和StringBuilder区别和选择
- 谈谈:程序集加载和反射
- 利用反射获得委托和事件以及创建委托实例和添加事件处理程序
- 谈谈:.Net中的序列化和反序列化
- C#设计模式
- UML类图符号 各种关系说明以及举例
- C#设计模式(1)——单例模式
- C#设计模式(2)——简单工厂模式
- C#设计模式(3)——工厂方法模式
- C#设计模式(4)——抽象工厂模式
- C#设计模式(5)——建造者模式(Builder Pattern)
- C#设计模式(6)——原型模式(Prototype Pattern)
- C#设计模式(7)——适配器模式(Adapter Pattern)
- C#设计模式(8)——桥接模式(Bridge Pattern)
- C#设计模式(9)——装饰者模式(Decorator Pattern)
- C#设计模式(10)——组合模式(Composite Pattern)
- C#设计模式(11)——外观模式(Facade Pattern)
- C#设计模式(12)——享元模式(Flyweight Pattern)
- C#设计模式(13)——代理模式(Proxy Pattern)
- C#设计模式(14)——模板方法模式(Template Method)
- C#设计模式(15)——命令模式(Command Pattern)
- C#设计模式(16)——迭代器模式(Iterator Pattern)
- C#设计模式(17)——观察者模式(Observer Pattern)
- C#设计模式(18)——中介者模式(Mediator Pattern)
- C#设计模式(19)——状态者模式(State Pattern)
- C#设计模式(20)——策略者模式(Stragety Pattern)
- C#设计模式(21)——责任链模式
- C#设计模式(22)——访问者模式(Vistor Pattern)
- C#设计模式(23)——备忘录模式(Memento Pattern)
- C#设计模式总结
- WPF快速入门系列
- WPF快速入门系列(1)——WPF布局概览
- WPF快速入门系列(2)——深入解析依赖属性
- WPF快速入门系列(3)——深入解析WPF事件机制
- WPF快速入门系列(4)——深入解析WPF绑定
- WPF快速入门系列(5)——深入解析WPF命令
- WPF快速入门系列(6)——WPF资源和样式
- WPF快速入门系列(7)——深入解析WPF模板
- WPF快速入门系列(8)——MVVM快速入门
- WPF快速入门系列(9)——WPF任务管理工具实现
- ASP.NET 开发
- ASP.NET 开发必备知识点(1):如何让Asp.net网站运行在自定义的Web服务器上
- ASP.NET 开发必备知识点(2):那些年追过的ASP.NET权限管理
- ASP.NET中实现回调
- 跟我一起学WCF
- 跟我一起学WCF(1)——MSMQ消息队列
- 跟我一起学WCF(2)——利用.NET Remoting技术开发分布式应用
- 跟我一起学WCF(3)——利用Web Services开发分布式应用
- 跟我一起学WCF(3)——利用Web Services开发分布式应用
- 跟我一起学WCF(4)——第一个WCF程序
- 跟我一起学WCF(5)——深入解析服务契约 上篇
- 跟我一起学WCF(6)——深入解析服务契约 下篇
- 跟我一起学WCF(7)——WCF数据契约与序列化详解
- 跟我一起学WCF(8)——WCF中Session、实例管理详解
- 跟我一起学WCF(9)——WCF回调操作的实现
- 跟我一起学WCF(10)——WCF中事务处理
- 跟我一起学WCF(11)——WCF中队列服务详解
- 跟我一起学WCF(12)——WCF中Rest服务入门
- 跟我一起学WCF(13)——WCF系列总结
- .NET领域驱动设计实战系列
- .NET领域驱动设计实战系列 专题一:前期准备之EF CodeFirst
- .NET领域驱动设计实战系列 专题二:结合领域驱动设计的面向服务架构来搭建网上书店
- .NET领域驱动设计实战系列 专题三:前期准备之规约模式(Specification Pattern)
- .NET领域驱动设计实战系列 专题四:前期准备之工作单元模式(Unit Of Work)
- .NET领域驱动设计实战系列 专题五:网上书店规约模式、工作单元模式的引入以及购物车的实现
- .NET领域驱动设计实战系列 专题六:DDD实践案例:网上书店订单功能的实现
- .NET领域驱动设计实战系列 专题七:DDD实践案例:引入事件驱动与中间件机制来实现后台管理功能
- .NET领域驱动设计实战系列 专题八:DDD案例:网上书店分布式消息队列和分布式缓存的实现
- .NET领域驱动设计实战系列 专题九:DDD案例:网上书店AOP和站点地图的实现
- .NET领域驱动设计实战系列 专题十:DDD扩展内容:全面剖析CQRS模式实现
- .NET领域驱动设计实战系列 专题十一:.NET 领域驱动设计实战系列总结