详解asp.net邮件收发系统

通过邮件客户端,不需要登录邮箱网站我们就能直接收发邮件。

1 系统分析
邮件收发是各个网站几乎必备的功能,在用户注册、邮箱确认、邮箱客服、找回密码等环节有典型应用。但是在上述例子中,基本都是用的邮件发送的功能,而邮件接收并管理的功能应用的相对较少。而且.NET 平台目前内置的完善的邮件接收的方法还是个空白。
使用一个邮箱服务器挂靠在Live.com上,域名为shuiqu.com的邮箱,实现邮件的收发。

1.1.1 系统目标
本系统需要实现的目标有以下5点。
⑴ 实现邮件发送。
⑵ 实现邮件接收。
(3) 实现对SSL的支持。
(4) 实现对附件的收发。
(5) 实现同时收发多个地址。

1.1.2 SMTP邮件发送原理
邮件的收发,需要相应的邮件收发服务器。目前流行的邮件收发基本都基于SMTP/POP3协议。虽然之前的Hotmail系列的邮箱不支持此设置,但是从Live.com邮箱开始,有了对SMTP/POP3协议的支持。
在.NET中发送邮件非常简单,只需要将账户信息、SMTP服务器信息、邮件信息通过MailMessage类实例和SmtpClient实例设置好,并调用SmtpClient实例的Send方法即可完成。

1.1.3 POP3邮件接收原理
要想使用POP3服务器,首先是登录服务器,获得认可,然后再调用各种API对邮件进行处理。而所有的对邮件的操作,则需要在客户端手动执行QUIT时再执行。
POP3常见的操作命令如表所示。

命令 参数 状态 描述
USER username 认可 此命令与下面的pass命令若成功,将导致状态转换
PASS password 认可
APOP Name,Digest 认可 Digest是MD5消息摘要
STAT None 处理 请求服务器发回关于邮箱的统计资料,如邮件总数和总字节数
UIDL [Msg#] 处理 返回邮件的唯一标识符,POP3会话的每个标识符都将是唯一的
LIST [Msg#] 处理 返回邮件数量和每个邮件的大小
RETR [Msg#] 处理 返回由参数标识的邮件的全部文本
DELE [Msg#] 处理 服务器将由参数标识的邮件标记为删除,由quit命令执行
RSET None 处理 服务器将重置所有标记为删除的邮件,用于撤消DELE命令
TOP [Msg#] 处理 服务器将返回由参数标识的邮件前n行内容,n必须是正整数
NOOP None 处理 服务器返回一个肯定的响应
QUIT None 更新 a.客户机希望结束这次会话
b.如果服务器处于“处理”状态,那么将进入“更新”状态以删除任何标记为删除的邮件
c.导致由“处理”状态到“更新”状态,又重返“认可”状态的转变
d.如果这个命令发出时服务器处于“认可”状态,则结束会话,不进行“更新”状态。

POP3服务器端命令的执行方式是:命令名+空格+参数+回车换行符(\r\n)。

1.2 系统设计
根据系统分析来逐一实现这些功能。
1.2.1 系统页面设计
重点讲解邮件收发的机制,界面只要求一个测试收发的页面.

1.2.2 定义基本信息类
建立一个MailFactory类,在里面封装3个基本类:
⑴ MailAccount:用于存放用户的账户信息,如用户名和密码等;
⑵ MailHost:用于存储SMTP或POP3服务器的详细设置,如服务器地址、端口、是否启用SSL等;
⑶ MailContent:是一个自定义的邮件类,用于结构化存储从POP3服务器获取到的零散数据。
MailFactory.cs代码如下(代码17-1.txt)。

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 ///
11 /// 账户登录基本信息
12 ///
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 ///
20 /// 收发服务器设置
21 ///
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 ///
29 /// 用于显示记录一条邮件的详细信息
30 ///
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 }

1.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 ///
13 /// Summary description for SendMail
14 ///
15 public class SendMail
16 {
17 public MailAccount Account { get; private set; }
18 public MailHost SendHost { get; private set; }
19 ///
20 /// 初始化账户基本信息
21 ///
22 ///
23 ///
24 ///
25 public SendMail(MailAccount account, MailHost sendHost)
26 {
27 this.Account = account;
28 this.SendHost = sendHost;
29 }
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 ///
68 /// 发送邮件
69 ///
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 }

1.2.4 建立接收邮件类
核心功能点
⑴ 由于.NET 没有内置此方法,所以导致了代码工作量加大,难度加大。
⑵ POP3有一组不同于SMTP的奇怪而复杂的参数传递方式,是以往的应用中不常见的,读者需要有一个适应的过程。
⑶ 为了实现POP3,还使用了Socket、Stream、SSL等新技术,可以说能让大家受益匪浅,但又颇费工夫。
为了实现邮件的接收,需要用以下3步来完成。
⑴ 封装一组方法,可以叫做“POP3服务器基层操作”,直接负责执行特定命令。
⑵ 封装一组方法,用来连接服务器、断开服务器,获取Stream、StreamReader等对象。
⑶ 封装各种POP3特定命令,并对各个命令的返回值进行深加工,以达到面向对象开发的要求。
所有的这些封装,全部在一个类中即可,因为为了完成POP3操作,需要大量地使用全局变量。

  1. POP3服务器基层操作

01 #region POP3服务器基层操作
02 ///
03 /// 向服务器发送一个4字母的字符串命令加上CRLF之后
04 /// 提交服务器执行,然后将响应结果存储在response中
05 ///
06 /// command to be sent to server
07 /// answer from server
08 /// false: server sent negative acknowledge, i.e. server could not execute command
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 response = sr.ReadLine();
28
29 }
30 catch (IOException ex)
31 {
32 throw;
33 }
34 if (response == null)
35 {
36 throw new Exception(“Server " + this.Pop3Server.Name + " has not responded, timeout has occured.”);
37 }
38 return (response.Length > 0 && response[0] == ‘+’);
39 }
40 ///
41 /// 使用RETR命令(retrieve)获取一个指定Id的邮件的信息
42 /// 返回的第1行信息是邮件的大小,其后内容可以通过StreamReader循环获取
43 ///
44 /// ID of message required
45 /// false: negative server respond, message not delivered
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 ///
58 /// 从POP3服务器获得一行响应,本方法主要用于判断服务器是否正常。返回“+ok…”说明正常,否则返回false
59 /// 响应格式为: +OK asdfkjahsf
60 ///
61 /// response from POP3 server
62 /// true: positive response
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 ///
82 /// 以多行模式获取邮件的其中一行数据,如果此返回数据不是以“.”开头的,说明读到最后一行了。
83 /// 如果返回false,说明已经读了邮件的最后一行了。此方法返回数据前,将所有response开头的“.”都去掉了
84 /// 调用此方法前,一般先调用了executeCommand方法,服务器已经准备数据待取
85 ///
86 /// 返回的当前行的数据。此值首先被置空,然后再被赋值
87 /// 如果返回false,说明已经读了邮件的最后一行了。
88 ///
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

  1. 连接到POP3服务器
    之所以将此块作为基层操作之上的内容,原因是POP3连接可以是SSL的,也可以不是。同时POP3为实现连接服务器还调用了上面代码块的内容。

01 #region 连接到POP3服务器
02 ///
03 /// 连接到POP3服务器。此方法是整个接收服务器的关键
04 ///
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 new Exception("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.\nRuntime Error: " + 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 ///
85 /// set POP3 connection state
86 ///
87 ///
88 protected void setPop3ConnectionState(Pop3ConnectionStateEnum State)
89 {
90 pop3ConnectionState = State;
91 }
92 ///
93 /// 判断当前的链接状态是否是指定的状态,如果不是,则抛出异常
94 ///
95 ///
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
117 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

  1. Email操作块
    此代码块直接使用上两块的成效,并在上述代码基础上,封装更具用户友好性的方法。代码如下。

01 #region EMAIL 操作
02 ///
03 /// 获取现在收件箱内的所有可用邮件的ID
04 ///
05 ///
06 public bool GetEmailIdList(out List EmailIds)
07 {
08 EnsureState(Pop3ConnectionStateEnum.Connected);
09 EmailIds = new List();
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 ///
34 /// 一行一行地获取邮件的全部内容
35 ///
36 /// Email to retrieve
37 /// ASCII string of complete message
38 ///
39 public bool GetEmailRawByRaw(int MessageNo, 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 ///
104 /// 将服务器上指定Id的邮件转移到垃圾箱,等客户端执行Update时再彻底删除
105 ///
106 ///
107 ///
108 public bool DeleteEmail(int msg_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;
117 }
118 ///
119 /// 统计邮箱的现有邮件的数量及邮箱大小
120 ///
121 ///
122 ///
123 ///
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 formatted response.” +
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 ///
149 /// 获取某个指定邮件的大小
150 ///
151 ///
152 ///
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 ///
167 /// 获取邮箱中现有邮件的唯一ID(unique Email id)。一个邮件的Email Id在程序中可以改动,但是unique Email id却是肯定不会变化的
168 ///
169 ///
170 ///
171 public bool GetUniqueEmailIdList(out List EmailIds)
172 {
173 EnsureState(Pop3ConnectionStateEnum.Connected);
174 EmailIds = new List();
175 //get server response status line
176 string response;
177 if (!executeCommand("UIDL ", out response))
178 {
179 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(new EmailUid(EmailId, responseSplit[1]));
190 }
191 }
192 return true;
193 }
194 #endregion

除了上述讲到的内容外,此类还有另外的变量定义,详情见附带的代码光盘。
1.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 idList;
14 if (receiveMail.GetEmailIdList(out idList))
15 {
16 List mails=new List();
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 }

1.3 运行系统

系统设计好了,下面来看系统运行的效果。
➊ 按【F5】或【Ctrl+F5】快捷键,在浏览器中运行该程序,如图所示输入想要发送的邮件及相关服务器设置,然后单击【发送】按钮。发送成功后,会在按钮下面显示“发送成功!”,否则便会显示错误信息。

➋ 进入邮箱,查看邮件收发情况,确认是否收到邮件,如图所示。

➌ 设置好POP3服务器之后,直接单击【接收】按钮,结果如图所示。

1.4 在我的网站中运用本系统

在实现邮件的发送、接收之后,便需要实现更多复杂的功能,比如:
⑴ 接收某个时间段内的邮件;
⑵ 查看垃圾箱中的邮件;
⑶ 将邮件移入、移出垃圾箱;
⑷ 彻底删除邮件;
⑸ 查看最新收到的邮件;
⑹ 将邮件标记为已读、未读,等等。
为了实现上述花哨而人性化的邮件管理功能,我们不仅需要POP3服务器的帮助,还需要在自己的邮件接收端建立一个存放邮件的设计完善的数据库,一方面实现POP3邮件操作,另一方面存储与服务器交互来的邮件。此工作在本章代码的基础上,请读者自行实现,问题应该不大。
1.5 开发常见问题及解决

  1. POP3邮件服务器登录时为什么使用了两次提交命令?
    对,POP3服务器登录有点不同于我们现在高级语言的登录习惯。POP3的指令都是以“指令名称+1个参数”的形式提交的,每次只能向服务器提交一个参数值,因此POP3的登录分两次:先提交用户名,检查其是否存在;如果没有此用户名便没有继续登录的必要了,直接返回登录失败的消息即可;如果POP3服务器确实有,再在一定的时间段内将密码提交到服务器验证即可。记住,提交用户名通过POP3验证后,服务器会启动一个针对这个邮件操作的对话,如果两次连接服务器的时间太长,对话会自动终止。
  2. 邮件发送时如何实现多个收件人、暗送、多个附件等常用功能?
    读者可以根据自己的需要改动代码中相关的参数,.NET封装的SMTP发送功能十分齐全,支持所有特殊的发送格式。对于不懂的参数,可以查看MSDN,上面有详细的参数讲解。
  3. POP3服务器返回的数据都是弱类型的吗?都是像实例代码中那样前几行是邮件的标头,一个空行后便是邮件内容吗?
    POP协议历经三代发展到了如今的POP3阶段,协议的内容与实现已经广为知晓,读者可以根据自己的需要去查找更专业的资料,弄清楚更多POP3的返回值格式以及解析的问题。

详解asp.net邮件收发系统相关推荐

  1. C#-Home / 详解Asp.Net Sql数据库连接字符串

    C#-Home / 详解Asp.Net Sql数据库连接字符串 2010-04-18 22:00 281人阅读 评论(0) 收藏 举报 1.连接数据库文件 <add name="Loc ...

  2. docker多个容器一起打包_详解Docker 容器基础系统镜像打包

    因为容器本身是共享宿主操作系统内核,所以容器基础系统镜像包本身就是一个标准的 Linux rootfs + 用户自定义的工具.根据这个思路,我们就可以构建一个自己的容器基础系统镜像. 构建标准的 Li ...

  3. linux mount命令衔接,Linux mount命令详解:挂载Linux系统外的文件

    Linux mount命令详解:挂载Linux系统外的文件 <Linux挂载>一节讲到,所有的硬件设备必须挂载之后才能使用,只不过,有些硬件设备(比如硬盘分区)在每次系统启动时会自动挂载, ...

  4. 计算机无法进系统咋办,详解电脑无法进入系统怎么办

    原标题:详解电脑无法进入系统怎么办 随着计算机的普及,许多人使用计算机.方便了我们的生活,也带来了一些问题,现在一些朋友遇到电脑进不了系统.原因是什么?有解决办法吗?接下来,我将教你如何解决计算机无法 ...

  5. 电商库存系统设计mysql_详解:电商系统库存逻辑的设计

    原标题:详解:电商系统库存逻辑的设计 本文主要对电商平台的订单系统相关库存逻辑进行了详细的阐述,一起来文中看看~ 一.库存的概念 电商的业务场景中订单的流程常常伴随着库存的变化:对于erp来说,库存可 ...

  6. 海康服务器装win7系统,详解win7旗舰版系统必须重装的四种情况

    在使用windows7操作系统过程中,难免会遇到一些故障,比如系统中毒或者无法进入系统等这些情况,可是很多人遇到这些问题并不会想到要重装系统,其实重装的方法很简单,而且也是可以彻底解决那些问题的最佳方 ...

  7. centos6 安装 mantisbt-1.2.8 —— (1) VMware Workstation 12 Pro 虚拟机安装步骤详解(window 10 系统)

    对于目前主流之一的VMware 虚拟机而言,一直是各位小伙伴的首选,而VMware 12 Pro 是截止目前最新的VMware Workstation 版本:偶在这里特地给刚刚入坑的小伙伴详细介绍介绍 ...

  8. 详解为什么32位系统只能用4G内存.

              既然是详解, 就从最基础的讲起了. 1. Bit(位)               Bit计算机是计算机最小的存储单位,  大家都知道计算机实质上都是用二进制数0或者1来存储数据的 ...

  9. cookie 操作详解 (asp.net javascript)

    (1)ASP.NET cookie 操作详解|cookie 写入.读取.修改.删除2008年10月18日     //写入     protected void Button2_Click(objec ...

最新文章

  1. B站超全分享!2万人收藏的免费计算机科学速成课
  2. CTO 说了,如果发现谁用 kill -9 关闭程序就开除
  3. Effective java 系列之更优雅的关闭资源-try-with-resources
  4. 十年IT运维谈(二)“0”和“100”
  5. [译]以PostgreSQL为例,谈join计算的代价
  6. [How TO]-How to install maven
  7. 算法与数据结构(一)
  8. 阿里云安全运营中心:DDoS攻击趁虚而入,通过代理攻击已成常态
  9. 分布式与人工智能课程(part3)--第三课
  10. c# 文件压缩、解压及下载
  11. postgre管理员 无法访问表_postgresql – 授予用户对所有表的访问权限
  12. VUE自学日志01-MVC和MVVM
  13. java chackbox,Java CheckBox.setText方法代码示例
  14. 虚拟机怎么查找服务器管理员,“你瞅瞅人王工家的VMware管理员~”
  15. IDEA项目搭建六——使用Eureka和Ribbon进行项目服务化
  16. 如何下载Mysql安装包?
  17. SQL2008.AdventureWorks_All_datebases x86下载
  18. 红外接收hs0038NEC协议
  19. IDEA 2022 CPU占用100%问题解决
  20. 1招提升Java单元测试ROI

热门文章

  1. 增速放缓、对手打劫,顺丰雄风不再?
  2. HDU 1290:献给杭电五十周年校庆的礼物
  3. 华为开发者联盟生态市场·首发上线
  4. 学以致用--游戏:孢子(Spore) 中 殖民地 最佳布局
  5. VS2019添加git源代码管理-增加VS版本 16.10.4的GIT管理
  6. 前端面试题之nina分享
  7. 【二 zedboard】PS和PL之间的交互
  8. Qumarion 3D技术轻松建模 动漫制作更省心
  9. iphone UINavigationController使用的一些技巧
  10. 读书笔记《硬件十万个为什么——开发流程篇》