文章教程

第22章邮件收发系统

8/31/2020 9:10:17 PM 人评论 次浏览

第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常见的操作命令如表所示。

figure_0494_0848

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

22.2 系统设计

本节视频教学录像:5分钟

本节根据系统分析来逐一实现这些功能。

22.2.1 系统页面设计

本章重点讲解邮件收发的机制,界面只要求一个测试收发的页面,具体设计请参阅附带光盘中的源码文件。

页面效果如图所示。

figure_0495_0849

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】快捷键,在浏览器中运行该程序,如图所示输入想要发送的邮件及相关服务器设置,然后单击【发送】按钮。发送成功后,会在按钮下面显示“发送成功!”,否则会显示错误信息。

figure_0510_0850

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

figure_0510_0851

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

figure_0510_0852

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的返回值格式以及解析的问题。

教程类别