引言:html
在咱们的日常工做中,邮件的发送和接收应该是咱们常常要使用到的功能的。所以知道电子邮件的应用程序的原理也是很是有必要的,在这一个专题中将介绍电子邮件应用程序的原理、电子邮件应用程序中涉及的协议和实现一个简答的电子邮件收发器程序。web
1、邮件应用程序基本知识编程
1.1 电子邮件原理及相关协议浏览器
说到电子邮件的原理,其实和咱们现实生活中寄邮件和寄包裹是同样的原理的。就让咱们先回顾下现实生活中寄邮件的流程吧——首先,咱们先写好信,信封上面写好收信人的地址,写信人的地址,而后把信放到寄信箱中,而后邮局的人会某个时候去这个信箱中的信取出来,而后邮局的人根据信封上写的收信人地址进行转发到当地的邮局,当地邮局而后把信寄到收信人的信箱中(寄包裹的话可能会电话联系,像咱们在淘宝,京东买的东西的,收货人就是经过电话联系同样),最后收信人会到本身的信箱中收取信件。上面大体是咱们平时生活中寄信的一个流程的。前面已经讲过电子邮件的原理和这个差很少的,下面就介绍了本专题中电子邮件的原理,你们能够和现实生活中的寄信过程进行对比下的,这样能够更加容易理解和掌握:服务器
咱们经过电子邮件应用(例如 基于客户端的Outlook电子邮件软件 和一些基于Web的电子邮件系统——新浪邮箱、谷歌邮箱、QQ邮箱等都属于电子邮件应用)将一封写好的邮件(至关于现实生活中的信,固然邮件也要写明收件人地址,邮件内容等信息的)经过电子邮件协议(SMTP,在后面的电子邮件相关协议中会介绍)发送到SMTP服务器(就是存储邮件的地方,至关于生活中的邮局同样),而后SMTP服务器根据收件人的地址经过SMTP协议转发到相应SMTP接收服务器上,(SMTP服务器进行转发至关于现实生活中邮局的人配送信的过程,配送到收件人当地的邮局,然而现实生活中邮局都是一家,因此能够相互识别——意思就是发送到当地邮局,当地邮局会接收,而且帮助你发送到指定人的信箱中,在网上上就是经过SMTP协议来规定这样的一个过程的,发送到别人的SMTP服务器上别人的服务器必需要认识发送来的邮件并接收)结束,接收端邮件服务器(POP3服务器)把邮件存放到接受者的电子信箱内(至关于当地邮局的人把信放到收信人的邮箱中),最后收件人能够登陆本身的电子信箱,再与POP3服务器进行链接,从POP3服务器上下载发送来的邮件,这样在收件人的电子信箱中就能够看到发送来的电子邮件了(这就是现实生活中收信人从本身的信箱中取信的一个过程)。网络
注:括号中都是我的的理解,若是有什么不对的地方还望你们指出来,我好及时更正。tcp
上面已经把电子邮件的原理和现实生活中寄信的过程进行对比,相信你们能够更加清楚电子邮件的原理和发送接收过程的,其实网络上的不少应用均可以以现实生活的例子去理解,这样的话我认为能够加深对知识的理解。下面就介绍下电子邮件中的相关协议的内容:ide
网络上的应用的核心就是协议,由于协议让网络上的客户端相互认识发生来的数据,因此电子邮件应用也不例外,也有相关的电子邮件协议来完成发送电子邮件和接收电子邮件的过程,这些协议主要是:SMTP(简单邮件传输协议,Simple Mail Transfer Protocol)、POP3(邮局协议,Post Office Protocol)和IMAP(网络邮件访问协议,Internet Message Access Protocol)。ui
1.2 邮件系统的分类this
邮件系统主要分为两类的——基于客户端的邮件系统和基于Web浏览器的邮件系统。Office OutLook就是基于客户端的邮件客户端系统,而像咱们常用的QQ邮箱、新浪、网易邮箱等都是属于基于Web浏览器的邮件系统,基于客户端的邮件系统的收发过程,经过下面的图片来描述(图片从网上摘下的):
发送方经过邮件客户端,将编辑好的邮件向邮件服务(SMTP服务器,在发送过程当中也叫发送端邮件服务器)发送,发送端邮件服务器根据收件人的地址来识别接收端邮件服务器(POP3服务器),而后向POP3服务器发送邮件信息,接收端邮件服务器将邮件存放在接收者的电子信箱中,并告知接收者有新邮件,接收者经过邮件客户端与POP3服务器链接后,就能够查看新邮件。
然而,基于Web浏览器的邮件系统与基于客户端的邮件系统不一样的地方有:
1.3 目前主要的电子邮件服务系统
电子邮件服务系统——就是向你们提供邮箱服务的服务系统,这样的系统固然是由专门的公司进行研发的,咱们通常叫这样的公司为邮件服务商,咱们日常使用的网易邮箱,新、Gmail邮箱等都是创建在电子邮件服务系统(这里个人理解是——咱们使用的新浪,网易等邮箱至关于现实生活中每一个人的信箱,经过信箱能够得到邮局来的信,一样道理经过邮箱能够得到邮件服务系统的邮件,这样电子邮件系统至关于邮局) 。如今主要电子邮件服务系统主要有下面几种:
2、.Net 平台对邮件发送功能的支持
在.NET类库中,在System.Net.Mail命名空间下定义了对邮件处理的类,这样使邮件的发送更加方便(这些类也就是对SMTP协议的封装,使咱们更好地区编程,只须要使用类中的方法和属性等去完成邮件的发送,避免写复杂的SMTP协议的命令),下面是一张在System.Net.Mail命名空间下对邮件发送的支持的类截图:
从图片中类的名字中也能够看出每一个类的做用的,在这里我就不一个介绍的, 你们能够参考MSDN去看每一个类的使用,而且我在后面程序的实现部分也会有详细的注释去介绍程序中使用到类的使用。从图中还能够i看出一点——就是只有SMTP的字样,却没有POP3这样的字样的,这说明.Net类库自己中并无提供对POP3协议的封装类,可是咱们可使用Jmail组件来完成从POP3服务器中收取邮件的功能,具体的使用将在后面的邮件收发器程序中邮件的接收部分介绍的。
3、邮件收发器程序的实现
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(发信人的地址)和Rcpt to: 794170314@qq.com
2. 首部
首部中经常使用的命令有:
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类的方法也是帮咱们完成发送命令功能而已的),下面是邮件发送功能的核心代码:
#region 邮件发送功能代码 // 添加附件 private void btnAddFile_Click(object sender, EventArgs e) { OpenFileDialog openFileDialog = new OpenFileDialog(); openFileDialog.CheckFileExists = true; // 只接受有效的文件名 openFileDialog.ValidateNames = true; // 容许一次选择多个文件做为附件 openFileDialog.Multiselect = true; openFileDialog.Filter = "全部文件(*.*)|*.*"; if (openFileDialog.ShowDialog() != DialogResult.OK) { return; } if (openFileDialog.FileNames.Length > 0) { // 由于这里容许选择多个文件,因此这里用AddRange而没有用Add方法 cmbAttachment.Items.AddRange(openFileDialog.FileNames); } } // 删除附件 private void btnDeleteFile_Click(object sender, EventArgs e) { int index = cmbAttachment.SelectedIndex; if (index == -1) { MessageBox.Show("请选择要删除的附件!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } else { cmbAttachment.Items.RemoveAt(index); } } // 发送邮件 private void btnSend_Click(object sender, EventArgs e) { this.Cursor = Cursors.WaitCursor; // 实例化一个发送的邮件 // 至关于与现实生活中先写信,程序中把信(邮件)抽象为邮件类了 MailMessage mailMessage = new MailMessage(); // 指明邮件发送的地址,主题,内容等信息 // 发信人的地址为登陆收发器的地址,这个收发器至关于咱们平时Web版的邮箱或者是OutLook中配置的邮箱 mailMessage.From = new MailAddress(tbxUserMail.Text); mailMessage.To.Add(txbSendTo.Text); mailMessage.Subject = txbSubject.Text; mailMessage.SubjectEncoding = Encoding.Default; mailMessage.Body = richtbxBody.Text; mailMessage.BodyEncoding = Encoding.Default; // 设置邮件正文不是Html格式的内容 mailMessage.IsBodyHtml = false; // 设置邮件的优先级为普通优先级 mailMessage.Priority = MailPriority.Normal; //mailMessage.ReplyTo = new MailAddress(tbxUserMail.Text); // 封装发送的附件 System.Net.Mail.Attachment attachment = null; if (cmbAttachment.Items.Count > 0) { for (int i = 0; i < cmbAttachment.Items.Count; i++) { string fileNamePath = cmbAttachment.Items[i].ToString(); string extName = Path.GetExtension(fileNamePath).ToLower(); if (extName == ".rar" || extName == ".zip") { attachment = new System.Net.Mail.Attachment(fileNamePath, MediaTypeNames.Application.Zip); } else { attachment = new System.Net.Mail.Attachment(fileNamePath,MediaTypeNames.Application.Octet); } // 表示MIMEContent-Disposition标头信息 // 对于ContentDisposition具体类的解释你们能够参考MSDN // 这里我就不重复贴出来了,给个地址: http://msdn.microsoft.com/zh-cn/library/System.Net.Mime.ContentDisposition.aspx (着重看备注部分) ContentDisposition cd = attachment.ContentDisposition; cd.CreationDate = File.GetCreationTime(fileNamePath); cd.ModificationDate = File.GetLastWriteTime(fileNamePath); cd.ReadDate = File.GetLastAccessTime(fileNamePath); // 把附件对象加入到邮件附件集合中 mailMessage.Attachments.Add(attachment); } } // 发送写好的邮件 try { // SmtpClient类用于将邮件发送到SMTP服务器 // 该类封装了SMTP协议的实现, // 经过该类能够简化发送邮件的过程,只须要调用该类的Send方法就能够发送邮件到SMTP服务器了。 smtpClient.Send(mailMessage); MessageBox.Show("邮件发送成功!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); } catch(SmtpException smtpError) { MessageBox.Show("邮件发送失败:[" + smtpError.StatusCode + "];[" + smtpError.Message+"];\r\n["+smtpError.StackTrace+"]." ,"错误",MessageBoxButtons.RetryCancel,MessageBoxIcon.Error); } finally { mailMessage.Dispose(); this.Cursor = Cursors.Default; } } #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命令给服务器,服务器接收到命令后再给出响应。操做中经常使用的命令有:
如:
客户端发送POP3命令: STAT
服务器响应命令: +OK 2 1340<BR>服务器响应命令:
|
如:
客户端发送POP3命令:LIST
服务器响应命令: +OK 2 message(1430 octect)
服务器响应命令:1 700
服务器响应命令:2 730
服务器响应命令:<一个空行>
|
如:
客户端发送POP3命令:RETR 1
服务器响应命令: 700 octets
服务器响应命令:<邮件头和内容>
服务器响应命令: <空行>
|
(3)更新状态——客户端发送QUIT命令后,此时就进入更新状态,POP3服务器释放在操做状态中取得的资源,并将逻辑删除的邮件进行物理删除,而后关闭与客户端的TCP链接。这样整个邮件处理的过程就结束了。
3.2.3 接收功能的实现代码
有了前面接收邮件过程的介绍,再参考代码的实现,相信你们能够更好的理解客户端从POP3服务器中获取邮件的过程的,因为.net类库并无帮咱们封装POP3协议的实现类,要实现邮件的获取能够采用发送命令的方式,也可使用Jmail组件,这个组件其实就是POP3协议的封装类,既然微软没有帮咱们作,其余公司帮咱们作好后来帮助咱们简单的实现邮件的接收的一个类库罢了。而后在使用这个组件的过程当中出现了好几个问题的,在源码中我都解释,你们能够下载源代码后查看的。
实现邮件接收的核心代码以下:
// 登陆邮箱(这里是本程序——邮件收发器) 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服务器来获取邮件列表的演示:
而后在邮件列表中选中一个邮件进行阅读,而后进行回复邮件的操做演示(邮件的发送均可以附加附件发送出去):
阅读邮件的界面:
回复邮件的界面:
同时点击发送按钮后,就能够把邮件发送到sina的SMTP服务器上,再由新浪的SMTP服务器转发到QQ的SMTP服务器,QQ的POP3服务器中QQ的SMTP服务器获取收到的邮件,当QQ用户输入正确的邮箱名和密码后就能够从QQ的POP3服务器上获取收到的邮件。
点击发送按钮后成功发送邮件的图片:
4、总结
介绍到这里,本专题的内容就已经介绍完了,但愿经过本专题可让你们明白邮件发送和接收的原理,而且能够自定义一个简单邮件收发器的功能的,在后面一专题将介绍FTP协议(文件传输协议),并实现一个简单的文件上传和下载的程序。
源代码下载地址: http://files.cnblogs.com/zhili/MailSendAndReceive.zip ,若是以为有帮助的话,还望你们推荐下,谢谢你们的支持
来自:http://www.cnblogs.com/zhili/archive/2012/09/24/MailSend_POP3_SMTP.html