第22章 邮件收发系统
本章视频教学录像:13分钟
通过邮件客户端,不需要登录邮箱网站就能直接收发邮件。
本章要点(已掌握的在方框中打钩)
□ 了解SMTP邮件发送原理
□ 了解 POP3 邮件接收原理
□ 实现 SMTP 邮件发送
□ 实现 POP3 邮件接收
□ 实现 POP3 常见邮件接收方法
22.1 系统分析
本节视频教学录像:3分钟
邮件收发是各个网站几乎必备的功能,在用户注册、邮箱确认、邮箱客服、找回密码等环节有典型应用。但是在上述例子中,基本都是用的邮件发送的功能,而邮件接收并管理的功能应用得相对较少。而且.NET 平台目前内置的完善的邮件接收的方法还是个空白。
本章使用一个邮箱服务器挂靠在163.com上,域名为163.com的邮箱,实现邮件的收发。
22.1.1 系统目标
本系统需要实现的目标有以下5点。
⑴实现邮件发送。
⑵实现邮件接收。
⑶实现对SSL的支持。
⑷实现对附件的收发。
⑸实现同时收发多个地址。
22.1.2 SMTP邮件发送原理
邮件的收发,需要相应的邮件收发服务器。目前流行的邮件收发基本都基于SMTP/POP3协议。
在.NET中发送邮件非常简单,只需要将账户信息、SMTP服务器信息、邮件信息通过MailMessage类实例和SmtpClient实例设置好,并调用SmtpClient实例的Send方法即可完成。
22.1.3 POP3邮件接收原理
提示
发送和接收最大的区别在哪里?
发送一个邮件。发送完之后就不再负责页面维护、增删改查等工作了,可以说发送邮件仅仅实现了邮件的增加,任务非常单一。可是邮件的删、增、查是通过什么完成的呢?
接收一个邮件。接收邮件时需要明确接收全部、最新的,还是垃圾箱、草稿箱中的?接收到客户端之后,对邮件修改甚至删除后,服务器端如何同步呢?这些都需要POP3服务器完成。
要想使用POP3服务器,首先是登录服务器,获得认可,然后再调用各种API对邮件进行处理。而对邮件的所有操作,则需要在客户端手动执行QUIT时再执行。
POP3常见的操作命令如表所示。
POP3服务器端命令的执行方式是:命令名+空格+参数+回车换行符(\r\n)。
22.2 系统设计
本节视频教学录像:5分钟
本节根据系统分析来逐一实现这些功能。
22.2.1 系统页面设计
本章重点讲解邮件收发的机制,界面只要求一个测试收发的页面,具体设计请参阅附带光盘中的源码文件。
页面效果如图所示。
22.2.2 定义基本信息类
建立一个MailFactory类,在里面封装以下3个基本类。
⑴MailAccount:用于存放用户的账户信息,如用户名和密码等;
⑵MailHost:用于存储SMTP或POP3服务器的详细设置,如服务器地址、端口、是否启用SSL等;
⑶MailContent:是一个自定义的邮件类,用于结构化存储从POP3服务器获取到的零散数据。
MailFactory.cs代码如下。
01 using System;
02 using System.Web;
03 //为实现相关功能引入的命名空间
04 using System.Text;
05 using System.Net.Mail;
06 using System.Net.Sockets;
07 using System.IO;
08 namespace MailFactory
09 {
10 ///<summary>
11 ///账户登录基本信息
12 ///< /summary>
13 public class MailAccount
14 {
15 public string AccountName {get; set; }
16 public string UserName {get; set; }
17 public string Password {get; set; }
18 }
19 ///<summary>
20 ///收发服务器设置
21 ///< /summary>
22 public class MailHost
23 {
24 public string Name {get; set; }
25 public int Port {get; set; }
26 public bool EnableSsl {get; set; }
27 }
28 ///<summary>
29 ///用于显示记录一条邮件的详细信息
30 ///< /summary>
31 public class MailContent
32 {
33 public int No {get; set; } //编号
34 public string Date {get; set; } //信件的发送日期
35 public string From {get; set; } //发件人
36 public string To {get; set; } //收件人
37 public string ContentType {get; set; } //内容类型
38 public string Subject {get; set; } //标题
39 public string Body {get; set; } //内容
40
41 }
42 }
22.2.3 建立发送邮件类
邮件发送是整个邮件操作中最简单的地方,只需简单调用 .NET方法即可实现,没有技术壁垒。代码如下。
01 using System;
02 using System.Collections.Generic;
03 using System.Linq;
04 using System.Web;
05 using System.Text;
06 using System.Net.Mail;
07 using System.Net.Sockets;
08 using System.IO;
09 using System.Net.Security;
邮件发送必须要引用的类
10 namespace MailFactory
11 {
12 ///<summary>
13 ///Summary description for SendMail
14 ///< /summary>
15 public class SendMail
16 {
22 public MailAccount Account {get;private set; }
18 public MailHost SendHost {get;private set; }
19 ///<summary>
20 ///初始化账户基本信息
21 ///< /summary>
22 ///<param name="account">< /param>
23 ///<param name="sendHost">< /param>
24 ///<param name="receiveHost">< /param>
25 public SendMail(MailAccount account,MailHost sendHost)
26 {
27 this.Account= account;
28 this.SendHost= sendHost;
29 } //通过此方法生成一个MailMessage对象,待发送时直接调用
30 public MailMessage GetMailMessage(string fromMail, string toMail, string ccMail, string subject, string body,bool ishtml,MailPriority priority,params string[] filenames)
31 {
32 MailMessage msg= new MailMessage();
33 msg.From= new MailAddress(fromMail);
34 //获取多个地址
35 string[] toMails= toMail.Split(';');
36 foreach (string s in toMails)
37 {
38 if (s != ""&& s != null)
39 {
40 msg.To.Add(s.Trim());
41 }
42 }
43 //添加CC地址
44 string[] ccMails= ccMail.Split(';');
45 foreach (string s in ccMails)
46 {
47 if (s != ""&& s != null)
48 {
49 msg.CC.Add(s.Trim());
50 }
51 }
52 msg.Subject= subject;
53 msg.Body=body;
54 msg.BodyEncoding=Encoding.UTF8;
55 msg.Priority=priority;
56 msg.IsBodyHtml= ishtml;
57 if (filenames != null)
58 {
59 foreach (string s in filenames)
60 if (s != ""&& s != null)
61 {
62 msg.Attachments.Add(new Attachment(s));
63 }
64 }
65 return msg;
66 }
67 ///<summary>
68 ///发送邮件
69 ///< /summary>
70 public string Send(MailMessage msg)
71 {
72 System.Net.Mail.SmtpClient sc = new SmtpClient(this.SendHost.Name, this.SendHost.Port);
73 sc.EnableSsl= this.SendHost.EnableSsl;
74 sc.Timeout= 3600000;
75 sc.UseDefaultCredentials= false;
76 sc.Credentials = new System.Net.NetworkCredential(this.Account.UserName, this.Account.Password);
77 sc.DeliveryMethod=SmtpDeliveryMethod.Network;
78 try
79 {
80 sc.Send(msg);
81 return "发送成功 !";
82 }
83 catch (Exception ex)
84 {
85 return "发送失败,原因如下:"+ ex.Message;
86 }
87 }
88 }
89 }
22.2.4 建立接收邮件类
本章的核心功能点便在此小节,原因有以下几点。
⑴由于.NET 没有内置此方法,所以导致了代码工作量加大,难度加大。
⑵POP3有一组不同于SMTP的奇怪而复杂的参数传递方式,是以往的应用中不常见的,读者需要有一个适应的过程。
⑶为了实现POP3,还使用了Socket、Stream、SSL等新技术,可以说能让大家受益匪浅,但又颇费工夫。
为了实现邮件的接收,需要用以下3步来完成。
⑴封装一组方法,可以叫做“POP3服务器基层操作”,直接负责执行特定命令。
⑵封装一组方法,用来连接服务器、断开服务器,获取Stream、StreamReader等对象。
⑶封装各种POP3特定命令,并对各个命令的返回值进行深加工,以达到面向对象开发的要求。
所有的这些封装,全部在一个类中即可,因为为了完成POP3操作,需要大量地使用全局变量。
1. POP3服务器基层操作
01 #region POP3服务器基层操作
02 ///<summary>
03 ///向服务器发送一个4个字母的字符串命令加在CRLF之后
04 ///提交服务器执行,然后将响应结果存储在 response中
05 ///< /summary>
06 ///<param name="command">command to be sent to server< /param>
07 ///<param name="response">answer from server< /param>
08 /// <returns>false: server sent negative acknowledge, i.e. server could not execute command< /returns>
09 private bool executeCommand(string command, out string response)
10 {
11 //send command to server
12 byte[] commandBytes = System.Text.Encoding.ASCII.GetBytes((command + CRLF).ToCharArray());
13 //bool isSupressThrow= false;
14 try
15 {
16 stream.Write(commandBytes, 0, commandBytes.Length);
17 }
18 catch (IOException ex)
19 {
20 throw;
21 }
22 stream.Flush();
23 //read response from server
24 response= null;
25 try
26 {
27 // sr/sw/ss等都是全局变量,调用ExecuteCommand之后便可以在任意方法中调用,以获取更详尽的服务器响应
28 response= sr.ReadLine();
29 }
30 catch (IOException ex)
31 {
32 throw;
33 }
34 if (response== null)
35 {
36 throw newException("Server "+ this.Pop3Server.Name+ " has not responded, timeout has occured.");
37 }
38 return (response.Length> 0&& response[0]== '+');
39 }
40 ///<summary>
41 ///使用RETR命令(retrieve)获取一个指定 Id的邮件的信息
42 ///返回的第1行信息是邮件的大小,其后内容可以通过StreamReader循环获取
43 ///< /summary>
44 ///<param name="MessageNo">ID ofmessage required< /param>
45 ///<returns>false: negative server respond,message not delivered< /returns>
46 protected bool SendRetrCommand(int MessageNo)
47 {
48 EnsureState(Pop3ConnectionStateEnum.Connected);
49 // retrieve mail with message number
50 string response;
51 if (!executeCommand("RETR "+MessageNo.ToString(), out response))
52 {
53 throw new Exception("获取第 "+MessageNo+ "条邮件的属性信息出错。");
54 }
55 return true;
56 }
57 ///<summary>
58 ///从POP3服务器获得一行响应,本方法主要用于判断服务器是否正常。返回“+ok...”说明正常,否则返回 false
59 ///<example>响应格式为 :+OK asdfkjahsf< /example>
60 ///< /summary>
61 ///<param name="response">response from POP3 server< /param>
62 ///<returns>true:positive response< /returns>
63 protected bool readSingleLine(out string response)
64 {
65 response= null;
66 try
67 {
68 response= sr.ReadLine();
69 }
70 catch (Exception ex)
71 {
72 string s= ex.Message;
73 }
74 if (response== null)
75 {
76 throw new Exception("Server "+ this.Pop3Server.Name+ " has not responded, timeout has occured.");
77 }
78 //CallTrace("Rx '{0}'", response);
79 return (response.Length> 0&& response[0]== '+');
80 }
81 ///<summary>
82 ///以多行模式获取邮件的其中一行数据,如果此返回数据不是以“.”开头的,说明读到最后一行了
83 ///如果返回 false,说明已经读了邮件的最后一行了。此方法返回数据前,将所有 response开头的“.”都去掉了
84 ///调用此方法前,一般先调用了executeCommand方法,服务器已经准备数据待取
85 ///< /summary>
86 /// <param name="response">返回的当前行的数据。此值首先被置空,然后再被赋值< /param>
87 ///<returns>如果返回 false,说明已经读了邮件的最后一行了< /returns>。
88 ///<returns>< /returns>
89 protected bool readMultiLine(out string response)
90 {
91
92 response= null;
93 response= sr.ReadLine();
94 if (response== null)
95 {
96 throw new Exception("服务器 "+ this.Pop3Server.Name+ "无反应,可能是由于超时。");
97 }
98 //除最后一行以外,其余的行都以“.“开头
99 if (response.Length> 0&& response[0]== '.')
100 {
101 if (response== ".")
102 {
103 //closing line found
104 return false;
105 }
106 //remove the first '.'
107 response= response.Substring(1, response.Length - 1);
108 }
109 return true;
110 }
111 #endregion
2. 连接到POP3服务器
之所以将此块作为基层操作之上的内容,原因是POP3连接可以是SSL的,也可以不是。同时POP3为实现连接服务器还调用了上面代码块的内容。
01 #region连接到POP3服务器
02 ///<summary>
03 ///连接到POP3服务器。此方法是整个接收服务器的关键
04 ///< /summary>
05 public void Connect()
06 {
07 //establish TCP connection
08 try
09 {
10 server= new TcpClient(this.Pop3Server.Name, this.Pop3Server.Port);
11 }
12 catch (Exception ex)
13 {
14 throw newException("Connection to server "+ this.Pop3Server.Name+ ",port "+ this.Pop3Server.Port+ " failed.\nRuntime Error: "+ ex.ToString());
15 }
16 if (this.Pop3Server.EnableSsl) //增加对SSL功能的支持
17 {
18 //get SSL stream
19 try
20 {
21 stream= new SslStream(server.GetStream(), false);
22 }
23 catch (Exception ex)
24 {
25 throw new Exception("Server " + this.Pop3Server.Name + " found, but cannot get SSL data stream.\nRuntime Error: "+ ex.ToString());
26 }
27 //perform SSL authentication
28 try
29 {
30 ((SslStream)stream).AuthenticateAsClient(this.Pop3Server.Name);
31 }
32 catch (Exception ex)
33 {
34 throw new Exception("Server "+ this.Pop3Server.Name+ " found,but problem with SSL Authentication.\nRuntime Error: "+ ex.ToString());
35 }
36 }
37 else
38 {
39 //create a stream to POP3 server without using SSL
40 try
41 {
42 stream= server.GetStream();
43 }
44 catch (Exception ex)
45 {
46 throw new Exception("Server " + this.Pop3Server.Name + " found, but cannot get data stream (without SSL).\nRuntime Error: "+ ex.ToString());
47 }
48 }
49 try
50 {
51 sr= new StreamReader(stream,Encoding.Default);
52 }
53 catch (Exception ex)
54 {
55 if (this.Pop3Server.EnableSsl)
56 {
57 throw new Exception("Server "+ this.Pop3Server.Name+ " found, but cannot read from SSL stream.\nRuntimeError: "+ ex.ToString());
58 }
59 else
60 {
61 throw new Exception("Server "+ this.Pop3Server.Name+ " found, but cannot read from stream (without SSL).\nRuntime Error: "+ ex.ToString());
62 }
63 }
64 //ready for authorisation
65 string response;
66 if (!readSingleLine(out response))
67 {
68 throw new Exception("Server " + this.Pop3Server.Name + " not ready to start AUTHORIZATION.\nMessage: "+ response);
69 }
70 setPop3ConnectionState(Pop3ConnectionStateEnum.Authorization);
71
72 //send user name
73 if (!executeCommand("USER "+ this.UserAccount.UserName, out response))
74 {
75 throw new Exception("Server "+ this.Pop3Server.Name+ "doesn't accept username '"+ this.UserAccount.UserName+ "'.\nMessage: "+ response);
76 }
77 //send password
78 if (!executeCommand("PASS "+ this.UserAccount.Password, out response))
79 {
80 throw new Exception("Server "+ this.Pop3Server.Name+ "doesn't accept password '"+ this.UserAccount.Password+ "' for user '"+ this.UserAccount.UserName+ "'.\nMessage: "+ response);
81 }
82 setPop3ConnectionState(Pop3ConnectionStateEnum.Connected);
83 }
84 ///<summary>
85 /// set POP3 connection state
86 ///< /summary>
87 ///<param name="State">< /param>
88 protected void setPop3ConnectionState(Pop3ConnectionStateEnumState)
89 {
90 pop3ConnectionState=State;
91 }
92 ///<summary>
93 ///判断当前的链接状态是否是指定的状态,如果不是,则抛出异常
94 ///< /summary>
95 ///<param name="requiredState">< /param>
96 protected void EnsureState(Pop3ConnectionStateEnum requiredState)
97 {
98 if (pop3ConnectionState != requiredState)
99 {
100 throw new Exception("目前指向服务器 "+ this.Pop3Server.Name+ "的连接状态是:"+pop3ConnectionState.ToString()+ "不是指定的 "+ requiredState.ToString());
101 }
102 }
103 public void Disconnect()
104 {
105 if (pop3ConnectionState==Pop3ConnectionStateEnum.Disconnected ||
106 pop3ConnectionState==Pop3ConnectionStateEnum.Closed)
107 {
108 return;
109 }
110 //ask server to end session and possibly to remove emails marked for deletion
111 try
112 {
113 string response;
114 if (executeCommand("QUIT", out response))
115 {
116 //server says everything is ok
122 setPop3ConnectionState(Pop3ConnectionStateEnum.Closed);
118 }
119 else
120 {
121 //server says there is a problem
122 setPop3ConnectionState(Pop3ConnectionStateEnum.Disconnected);
123 }
124 }
125 finally
126 {
127 //close connection
128 if (stream != null)
129 {
130 stream.Close();
131 }
132 sr.Close();
133 }
134 }
135 #endregion
3. Email操作块
此代码块直接使用上两块的成效,并在上述代码基础上,封装更具用户友好性的方法。代码如下。
01 #region EMAIL操作
02 ///<summary>
03 ///获取现在收件箱内的所有可用邮件的 ID
04 ///< /summary>
05 ///<returns>< /returns>
06 public bool GetEmailIdList(out List<int>EmailIds)
07 {
08 EnsureState(Pop3ConnectionStateEnum.Connected);
09 EmailIds= new List<int>();
10 //get server response status line
11 string response;
12 if (!executeCommand("LIST", out response))
13 {
14 return false;
15 }
16 //get every email id
17 int EmailId;
18 while (readMultiLine(out response))
19 {
20 if (int.TryParse(response.Split(' ')[0], out EmailId))
21 {
22 EmailIds.Add(EmailId);
23 }
24 else
25 {
26 //CallWarning("GetEmailIdList", response, "first characters should be integer(EmailId)");
27 }
28 }
29 //TraceFrom("{0} email ids received",EmailIds.Count);
30 return true;
31 }
32
33 ///<summary>
34 ///一行一行地获取邮件的全部内容
35 ///< /summary>
36 ///<param name="MessageNo">Email to retrieve< /param>
37 ///<param name="EmailText">ASCII string of complete message< /param>
38 ///<returns>< /returns>
39 public bool GetEmailRawByRaw(intMessageNo, out MailContent mail)
40 {
41 //先向服务器发送一个“RETR int“命令,查看邮件是否存在
42 if (!SendRetrCommand(MessageNo))
43 {
44 mail= null;
45 return false;
46 }
47 mail= new MailContent();
48 mail.No=MessageNo;
49 #region获取邮件头中的信息
50 string mailHead=null;
51 bool bEnd=false ;
52 do{
53 bEnd=readMultiLine(out mailHead);
54 if (!bEnd)
55 {
56 mail= null;
57 return false;
58 }
59 if (mailHead != ""&&mailHead != null)
60 {
61 int index=mailHead.IndexOf(':');
62 string sType=mailHead.Substring(0, index).ToUpper();
63 string sValue=mailHead.Substring(index+ 1).Trim();
64 switch (sType)
65 {
66
67 case "FROM":
68 mail.From= sValue;
69 //发信人
70 break;
71 case "TO":
72 mail.To= sValue;
73 //收信人
74 break;
75 case "DATE":
76 mail.Date= sValue;
77 //信件的发送日期
78 break;
79 case "CONTENT-TYPE":
80 mail.ContentType= sValue;
81 //信件编码类型
82 break;
83 case "SUBJECT":
84 mail.Subject= sValue;
85 //主题
86 break;
87 }
88 }
89 }
90 while (bEnd && mailHead != "");
91 #endregion
92 #region获取邮件Body内容
93 string response;
94 StringBuilder mailBody= new StringBuilder("");
95 while (readMultiLine(out response))
96 {
97 mailBody.Append(response);
98 }
99 mail.Body=mailBody.ToString();
100 #endregion
101 return true;
102 }
103 ///<summary>
104 ///将服务器上指定 Id的邮件转移到垃圾箱,等客户端执行Update时再彻底删除
105 ///< /summary>
106 ///<param name="msg_number">< /param>
107 ///<returns>< /returns>
108 public bool DeleteEmail(intmsg_number)
109 {
110 EnsureState(Pop3ConnectionStateEnum.Connected);
111 string response;
112 if (!executeCommand("DELE "+msg_number.ToString(), out response))
113 {
114 return false;
115 }
116 return true;
122 }
118 ///<summary>
119 ///统计邮箱的现有邮件的数量及邮箱大小
120 ///< /summary>
121 ///<param name="NumberOfMails">< /param>
122 ///<param name="MailboxSize">< /param>
123 ///<returns>< /returns>
124 public bool GetMailboxStats(out int NumberOfMails, out int MailboxSize)
125 {
126 EnsureState(Pop3ConnectionStateEnum.Connected);
127 //interpret response
128 string response;
129 NumberOfMails= 0;
130 MailboxSize= 0;
131 if (executeCommand("STAT", out response))
132 {
133 //got a positive response
134 string[] responseParts= response.Split(' ');
135 if (responseParts.Length< 2)
136 {
137 //response format wrong
138 throw new Exception("Server "+ this.Pop3Server.Name+ " sends illegally formattedresponse."+
139 "\nExpected format:+OK int int"+
140 "\nReceived response: "+ response);
141 }
142 NumberOfMails= int.Parse(responseParts[1]);
143 MailboxSize= int.Parse(responseParts[2]);
144 return true;
145 }
146 return false;
147 }
148 ///<summary>
149 ///获取某个指定邮件的大小
150 ///< /summary>
151 ///<param name="msg_number">< /param>
152 ///<returns>< /returns>
153 public int GetEmailSize(int msg_number)
154 {
155 EnsureState(Pop3ConnectionStateEnum.Connected);
156 string response;
157 executeCommand("LIST "+msg_number.ToString(), out response);
158 int EmailSize= 0;
159 string[] responseSplit= response.Split(' ');
160 if (responseSplit.Length< 2 || !int.TryParse(responseSplit[2], out EmailSize))
161 {
162 throw new Exception("获取数据失败 ");
163 }
164 return EmailSize;
165 }
166 ///<summary>
167 ///获取邮箱中现有邮件的唯一ID(unique Email id)。一个邮件的Email Id在程序中可以改动,但是unique Email id却是肯定不会变化的
168 ///< /summary>
169 ///<param name="EmailIds">< /param>
220 ///<returns>< /returns>
221 public bool GetUniqueEmailIdList(out List<EmailUid>EmailIds)
222 {
223 EnsureState(Pop3ConnectionStateEnum.Connected);
224 EmailIds= new List<EmailUid>();
225 //get server response status line
226 string response;
227 if (!executeCommand("UIDL ", out response))
228 {
229 return false;
180 }
181 //get every email unique id
182 int EmailId;
183 while (readMultiLine(out response))
184 {
185 string[] responseSplit= response.Split(' ');
186
187 if (responseSplit.Length>=2&&int.TryParse(responseSplit[0], out EmailId))
188 {
189 EmailIds.Add(newEmailUid(EmailId, responseSplit[1]));
190 }
191 }
192 return true;
193 }
194 #endregion
除了上述讲到的内容外,此类还有另外的变量定义,详情见附带的代码光盘。
22.2.5 调用接收邮件类
下面是Default.aspx页面下,单击【接收】按钮时执行的代码。
01 protected void btnReceive_Click(object sender,EventArgs e)
02 {
03 MailAccount account= new MailAccount(
04 this.accountName.Text,
05 this.userName.Text,
06 this.password.Text);
07 MailHost receiveHost= new MailHost(
08 this.pop3Host.Text,
09 Convert.ToInt32(this.pop3Port.Text),
10 this.pop3SSLEnable.Checked);
11 ReceiveMail receiveMail= new ReceiveMail(account, receiveHost);
12 receiveMail.Connect();
13 List<int> idList;
14 if (receiveMail.GetEmailIdList(out idList))
15 {
16 List<MailContent>mails=new List<MailContent>();
17 foreach (int i in idList)
18 {
19 MailContent mail;
20 if (receiveMail.GetEmailRawByRaw(i, out mail))
21 {
22 mails.Add(mail);
23 }
24 }
25 DetailsView1.DataSource=mails;
26 DetailsView1.DataBind();
27 }
28 //receiveMail.
29 }
22.3 运行系统
本节视频教学录像:2分钟
系统设计好了,下面来看系统运行的效果。
⑴按【F5】或【Ctrl+F5】快捷键,在浏览器中运行该程序,如图所示输入想要发送的邮件及相关服务器设置,然后单击【发送】按钮。发送成功后,会在按钮下面显示“发送成功!”,否则会显示错误信息。
⑵进入邮箱,查看邮件收发情况,确认是否收到邮件,如图所示。
⑶设置好POP3服务器之后,直接单击【接收】按钮,结果如图所示。
22.4 在我的网站中运用本系统
本节视频教学录像:1分钟
在实现邮件的发送、接收之后,需要实现更多复杂的功能,比如:
⑴接收某个时间段内的邮件;
⑵查看垃圾箱中的邮件;
⑶将邮件移入、移出垃圾箱;
⑷彻底删除邮件;
⑸查看最新收到的邮件;
⑹将邮件标记为已读、未读,等等。
为了实现上述复杂而人性化的邮件管理功能,我们不仅需要POP3服务器的帮助,还需要在自己的邮件接收端建立一个存放邮件的设计完善的数据库,一方面实现POP3邮件操作,另一方面存储与服务器交互来的邮件。读者可以在本章代码的基础上自行实现此功能。
22.5 开发过程中的常见问题及解决方式
本节视频教学录像:3分钟
1. POP3邮件服务器登录时为什么使用了两次提交命令?
POP3服务器登录不同于高级语言的登录习惯。POP3的指令都是以“指令名称+1个参数”的形式提交的,每次只能向服务器提交一个参数值,因此POP3的登录分两次:先提交用户名,检查其是否存在;如果没有此用户名便没有继续登录的必要了,直接返回登录失败的消息即可;如果确实有POP3服务器,再在一定的时间段内将密码提交到服务器验证即可。记住,提交用户名通过POP3验证后,服务器会启动一个针对这个邮件操作的对话,如果两次连接服务器的时间太长,对话会自动终止。
2. 邮件发送时如何实现多个收件人、暗送、多个附件等常用功能?
读者可以根据自己的需要改动代码中相关的参数,.NET封装的SMTP发送功能十分齐全,支持所有特殊的发送格式。对于不懂的参数,可以查看MSDN,上面有详细的参数讲解。
3. POP3服务器返回的数据都是弱类型的吗?都是像实例代码中那样前几行是邮件的标头,一个空行后便是邮件内容吗?
POP协议历经三代发展到了如今的POP3阶段,协议的内容与实现已经广为知晓,读者可以根据自己的需要去查找更专业的资料,了解更多POP3的返回值格式以及解析的问题。