邮件发送需考虑很多因素,包括发送邮件客户端(一般编码实现),发送和接收邮件服务器设置等。如果使用第三方邮件服务器作为发送服务器,就需要考虑该服务器的发送限制,(如发送邮件时间间隔,单位时间内发送邮件数量,是否使用安全连接SSL),同时无论使用第三方还是自己的邮件服务器都还需要考虑接收邮件服务器的限制。为理清思路,下面我们简单回顾电子邮件系统的基本网络结构和邮件发送接收流程。

一、电子邮件系统的基本网络结构

如下图:

邮件发送接收一般经过以下几个节点:

  • 发送邮件客户端(Mail User Agent, MUA) : Formail, Outlook, Webmail, C# Code, Java Code, etc.
  • 发送邮件服务器(Mail Transfer Agent, MTA) : hMailServer, Exchange, TurboMail, etc.
  • 接收邮件服务器(Mail Transfer Agent, MTA)
  • 接收邮件客户端(Mail User Agent, MUA)

发送过程中客户端与服务器及服务器之间使用SMTP协议,在接收过程中客户端与服务端之间使用POP3或IMAP(POP3的替代协议,支持邮件摘要显示和脱机操作)。邮件发送可简单认为是一种文件传输,但与FTP实时文件传输不同,各邮件服务器会保存邮件文件本身,直至被下一个邮件服务器或客户端接收,类似异步与同步的差别。

由上可知,为顺利发送和接受邮件,客户端设置或编码需要严格适应邮件服务器的要求。对于发送邮件需明确:SMTP服务器地址和端口(默认端口25),是否使用安全连接(SSL),验证凭据(用户和密码),及更加细节的邮件格式,邮件编码方式等;对于接收邮件需明确:POP3或IMAP服务器地址和端口(POP3默认端口110,IMAP默认端口143),是否使用安全连接(SSL),验证凭据(用户和密码)

二、C#下发送邮件组件及测试

C#下发送邮件的组件使用较为普遍的有以下三个:System.Net.Mail, OpenSmtp, LumiSoft.Net。下面我们就分别对他们进行测试。

发送邮件至少需要发送邮件服务器信息和邮件信息,因此我们建立Host和Mail两个配置类。

    public class ConfigHost{public string Server { get; set; }public int Port { get; set; }public string Username { get; set; }public string Password { get; set; }public bool EnableSsl { get; set; }}public class ConfigMail{public string From { get; set; }public string[] To { get; set; }public string Subject { get; set; }public string Body { get; set; }public string[] Attachments { get; set; }public string[] Resources { get; set; }}

同时定义一个统一的接口ISendMail,以方便测试和比较。

    public interface ISendMail{void CreateHost(ConfigHost host);void CreateMail(ConfigMail mail);void CreateMultiMail(ConfigMail mail);void SendMail();}

1、使用System.Net.Mail

System.Net.Mail属于.Net Framework 的一部分,.Net2.0以后可以使用这个组件。

    using System.Net.Mail;public class UseNetMail : ISendMail{private MailMessage Mail { get; set; }private SmtpClient Host { get; set; }public void CreateHost(ConfigHost host){Host = new SmtpClient(host.Server, host.Port);Host.Credentials = new System.Net.NetworkCredential(host.Username, host.Password);Host.EnableSsl = host.EnableSsl;}public void CreateMail(ConfigMail mail){Mail = new MailMessage();Mail.From = new MailAddress(mail.From);foreach (var t in mail.To)Mail.To.Add(t);Mail.Subject = mail.Subject;Mail.Body = mail.Body;Mail.IsBodyHtml = true;Mail.BodyEncoding = System.Text.Encoding.UTF8;}public void CreateMultiMail(ConfigMail mail){CreateMail(mail);Mail.AlternateViews.Add(AlternateView.CreateAlternateViewFromString("If you see this message, it means that your mail client does not support html.", Encoding.UTF8, "text/plain"));var html = AlternateView.CreateAlternateViewFromString(mail.Body, Encoding.UTF8, "text/html");foreach (string resource in mail.Resources){var image = new LinkedResource(resource, "image/jpeg");image.ContentId = Convert.ToBase64String(Encoding.Default.GetBytes(Path.GetFileName(resource)));html.LinkedResources.Add(image);}Mail.AlternateViews.Add(html);foreach (var attachment in mail.Attachments){Mail.Attachments.Add(new Attachment(attachment));}}public void SendMail(){if (Host != null && Mail != null)Host.Send(Mail);elsethrow new Exception("These is not a host to send mail or there is not a mail need to be sent.");}}

2、使用OpenSmtp
开源的发送邮件组件,可以在这里获得源码。但是OpenSmtp目前不支持SSL。
    using OpenSmtp.Mail;public class UseOpenSmtp : ISendMail{private MailMessage Mail { get; set; }private Smtp Host { get; set; }public void CreateHost(ConfigHost host){Host = new Smtp(host.Server, host.Username, host.Password, host.Port);}public void CreateMail(ConfigMail mail){Mail = new MailMessage();Mail.From = new EmailAddress(mail.From);foreach (var t in mail.To)Mail.AddRecipient(t, AddressType.To);Mail.HtmlBody = mail.Body;Mail.Subject = mail.Subject;Mail.Charset = "UTF-8";}public void CreateMultiMail(ConfigMail mail){CreateMail(mail);foreach (var attachment in mail.Attachments){Mail.AddAttachment(attachment);}foreach (var resource in mail.Resources){Mail.AddImage(resource, Convert.ToBase64String(Encoding.Default.GetBytes(Path.GetFileName(resource))));            }}public void SendMail(){if (Host != null && Mail != null)Host.SendMail(Mail);elsethrow new Exception("These is not a host to send mail or there is not a mail need to be sent.");}

3、使用LumiSoft.Net

LumiSoft.Net是非常强大的开源组件,不仅仅发送邮件,同样也可用于接收邮件,是个人认为最好的开源组件了。在这里可以详细了解LumiSoft.Net组件的命名空间,也可以在这里下载其源码和样例。

    using LumiSoft.Net.SMTP.Client;using LumiSoft.Net.AUTH;using LumiSoft.Net.Mail;using LumiSoft.Net.MIME;public class UseLumiSoft : ISendMail{private SMTP_Client Host { get; set; }private Mail_Message Mail { get; set; }public void CreateHost(ConfigHost host){Host = new SMTP_Client();Host.Connect(host.Server, host.Port, host.EnableSsl);Host.EhloHelo(host.Server);Host.Auth(Host.AuthGetStrongestMethod(host.Username, host.Password));}public void CreateMail(ConfigMail mail){Mail = new Mail_Message();Mail.Subject = mail.Subject;Mail.From = new Mail_t_MailboxList();Mail.From.Add(new Mail_t_Mailbox(mail.From, mail.From));Mail.To = new Mail_t_AddressList();foreach (var to in mail.To){Mail.To.Add(new Mail_t_Mailbox(to, to));}var body = new MIME_b_Text(MIME_MediaTypes.Text.html);Mail.Body = body; //Need to be assigned first or will throw "Body must be bounded to some entity first" exception.body.SetText(MIME_TransferEncodings.Base64, Encoding.UTF8, mail.Body);}public void CreateMultiMail(ConfigMail mail){CreateMail(mail);var contentTypeMixed = new MIME_h_ContentType(MIME_MediaTypes.Multipart.mixed);contentTypeMixed.Param_Boundary = Guid.NewGuid().ToString().Replace("-", "_");var multipartMixed = new MIME_b_MultipartMixed(contentTypeMixed);Mail.Body = multipartMixed;//Create a entity to hold multipart/alternative bodyvar entityAlternative = new MIME_Entity();var contentTypeAlternative = new MIME_h_ContentType(MIME_MediaTypes.Multipart.alternative);contentTypeAlternative.Param_Boundary = Guid.NewGuid().ToString().Replace("-", "_");var multipartAlternative = new MIME_b_MultipartAlternative(contentTypeAlternative);entityAlternative.Body = multipartAlternative;multipartMixed.BodyParts.Add(entityAlternative);var entityTextPlain = new MIME_Entity();var plain = new MIME_b_Text(MIME_MediaTypes.Text.plain);entityTextPlain.Body = plain;plain.SetText(MIME_TransferEncodings.Base64, Encoding.UTF8, "If you see this message, it means that your mail client does not support html.");multipartAlternative.BodyParts.Add(entityTextPlain);var entityTextHtml = new MIME_Entity();var html = new MIME_b_Text(MIME_MediaTypes.Text.html);entityTextHtml.Body = html;html.SetText(MIME_TransferEncodings.Base64, Encoding.UTF8, mail.Body);multipartAlternative.BodyParts.Add(entityTextHtml);foreach (string attachment in mail.Attachments){multipartMixed.BodyParts.Add(Mail_Message.CreateAttachment(attachment));}foreach (string resource in mail.Resources){var entity = new MIME_Entity();entity.ContentDisposition = new MIME_h_ContentDisposition(MIME_DispositionTypes.Inline);entity.ContentID = Convert.ToBase64String(Encoding.Default.GetBytes(Path.GetFileName(resource))); //eg.<img src="cid:ContentID"/>var image = new MIME_b_Image(MIME_MediaTypes.Image.jpeg);entity.Body = image;image.SetDataFromFile(resource, MIME_TransferEncodings.Base64);multipartMixed.BodyParts.Add(entity);}}public void SendMail(){if (Host != null && Mail != null){foreach (Mail_t_Mailbox from in Mail.From.ToArray()){Host.MailFrom(from.Address, -1);}foreach (Mail_t_Mailbox to in Mail.To){Host.RcptTo(to.Address);}using (var stream = new MemoryStream()){Mail.ToStream(stream, new MIME_Encoding_EncodedWord(MIME_EncodedWordEncoding.Q, Encoding.UTF8), Encoding.UTF8);stream.Position = 0;//Need to be reset to 0, otherwise nothing will be sent;Host.SendMessage(stream);Host.Disconnect();}}elsethrow new Exception("These is not a host to send mail or there is not a mail need to be sent.");}}

阅读LumiSoft.Net的源代码,可以看到LumiSoft.Net编程严格遵循了RFC(Request For Comments)定义的协议规范。通过阅读这些源码对于了解RFC和其中关于邮件网络协议规范也是非常有帮助的。如果想查阅RFC文档可以通过这个链接。

在上面的代码中MIME_MediaTypes类,MIME_TransferEncodings类和Encoding类(System.Text.Encoding)都是或类似于枚举,设置了邮件内容的编码方式或解析方式,这个几个类从根本上决定了邮件的正常传输和显示。MIME_TransferEncodings类设置了文件传输编码,决定邮件头中的Content-Transfer-Encoding字段的值及其他需要传输编码字段的编码方式(如标题中的多国语言)。MIME_MediaTypes类设置邮件各部分内容的类型,决定邮件中Content-Type字段的值。而Encoding类不用说,决定了charset的值。关于这些设置的具体作用下文还将提到,这里略过。

4、测试

下表是通过网络搜集的各大SMTP服务器的配置情况,可以选择使用这些配置进行测试:

服务商 SMTP地址 SMTP端口 EnableSsl
gmail smtp.google.com 25, 465 or 587 true
126 smtp.126.com 25 false
163 smtp.126.com 25 false
hotmail smtp.live.com 25 true
sina smtp.sina.com 25 false
sohu smtp.sohu.com 25 false

新建控制台应用程序,测试发送只包含正文的简单邮件:

    class Program{static void Main(string[] args){           var h1 = new ConfigHost(){Server = "smtp.gmail.com",Port = 465,Username = "******@gmail.com",Password = "******",EnableSsl = true};
var m1 = new ConfigMail(){Subject = "Test",Body = "Just a test.",From = "******@gmail.com",To = new string[] { "******@gmail.com" }, 
            };var agents = new List<ISendMail>() { new UseNetMail(), new UseOpenSmtp(), new UseLumiSoft() };foreach (var agent in agents){var output = "Send m1 via h1 " + agent.GetType().Name + " ";Console.WriteLine(output + "start");try{agent.CreateHost(h1);m1.Subject = output;        agent.CreateMail(m1);agent.SendMail();Console.WriteLine(output + "success");}catch (Exception ex){Console.WriteLine(ex.Message);}Console.WriteLine(output + "end");Console.WriteLine("-----------------------------------");}Console.Read();}}

通过gmail发送邮件时,OpenSmtp由于不支持SSL发送失败,NetMail使用587端口能够成功发送,LumiSoft使用465端口能够成功发送。查阅Gmail相关文档,描述说Gmail的465端口使用SSL协议,而587端口使用TLS协议,但587是需要STARTTLS命令支持才能提升为TLS。在命令提示符下测试发现的确需要在发送STARTTLS命令后才能使用TLS协议:

> telnet smtp.gmail.com 587
220 mx.google.com ESMTP o5sm40420786eeg.8 - gsmtp
EHLO g1
250-mx.google.com at your service, [173.231.8.212]
250-SIZE 35882577
250-8BITMIME
250-STARTTLS
250-ENHANCEDSTATUSCODES
250 CHUNKING
AUTH LOGIN
530 5.7.0 Must issue a STARTTLS command first. o5sm40420786eeg.8 – gsmtpSTARTTLS220 
STARTTLS
2.0.0 Ready to start TLS
QUIT

对于TLS与STARTTLS人们经常搞混,这里找到一篇关于它们的解释,请点击这里。

因而LumiSoft如果连接gmail服务器时还需明确发送STARTTLS命令,已经发现LumiSoft有相关方法SMTP_Client.StartTLS(),连接gmail相较其他smtp服务器还是较为复杂些。另外一些服务器要求邮件配置中的Username必须与From相一致,需要特别注意。

测试发送带附件和内嵌资源的邮件:

    class Program{static void Main(string[] args){var h2 = new ConfigHost(){Server = "smtp.163.com",Port = 25,Username = "******@163.com",Password = "******",EnableSsl = false};var m2 = new ConfigMail(){Subject = "Test",Body = "Just a test. <br/><img src='cid:" + Convert.ToBase64String(Encoding.Default.GetBytes("Resource.jpg")) + "' alt=''/> ", 
                From = "******@163.com",To = new string[] { "******@163.com" }, 
                Attachments = new string[] { @"E:\Test\SendMail\Attachment.pdf" }, 
                Resources = new string[] { @"E:\Test\SendMail\Resource.jpg" } };var agents = new List<ISendMail>() { new UseNetMail(), new UseOpenSmtp(), new UseLumiSoft() };foreach (var agent in agents){var output = "Send m2 via h2 " + agent.GetType().Name + " ";Console.WriteLine(output + "start");try{agent.CreateHost(h2);m2.Subject = output;        agent.CreateMultiMail(m2);agent.SendMail();Console.WriteLine(output + "success");}catch (Exception ex){Console.WriteLine(ex.Message);}Console.WriteLine(output + "end");Console.WriteLine("-----------------------------------");}Console.Read();}}

C#邮件发送问题(一)相关推荐

  1. 邮件发送类,支持HTML格式,支持优先级设置

    www.chinacs.net  2002-5-9  中文C#技术站 邮件发送类,支持HTML格式,支持优先级设置.通过SOCKET类实现的 using System; using System.Te ...

  2. 使用Spring实现邮件发送

    2019独角兽企业重金招聘Python工程师标准>>> 这两天写个小程序需要使用邮件发送的功能,在网上搜索了一帮子文章,感觉还是使用Spring的邮件发送功能比较方便,哈哈,懒人就这 ...

  3. python邮件发送哪个好_最全总结!聊聊 Python 发送邮件的几种方式

    1. 前言 邮件,作为最正式规范的沟通方式,在日常办公过程中经常被用到 我们都知道 Python内置了对 SMTP 的支持,可以发送纯文本.富文本.HTML 等格式的邮件 本文将聊聊利用 Python ...

  4. CentOS搭建msmtp+mutt实现邮件发送

    1:搭建配置msmtp 下载msmtp包: 官方地址:http://msmtp.sourceforge.net/download.html 编译,安装(官方下载的包为tar.xz格式): #xz -d ...

  5. 利用phpmailer类邮件发送

    <?phprequire("class.phpmailer.php"); //下载的文件必须放在该文件所在目录$mail = new PHPMailer(); //建立邮件发 ...

  6. python 数据库查询结果邮件提醒_Python实现的查询mysql数据库并通过邮件发送信息功能...

    本文实例讲述了Python实现的查询mysql数据库并通过邮件发送信息功能.分享给大家供大家参考,具体如下: 这里使用Python查询mysql数据库,并通过邮件发送宕机信息. Python代码如下: ...

  7. JavaMail邮件发送不成功的那些坑人情况及分析说明

    前言   JavaMail的使用本身并不难,网上有不少案例,简单易懂,而且有详细的中文注解.但是由于JavaMail的机制设置不够完善,特别是异常出错时的参考信息太少,给初学者造成了不少麻烦,而我就是 ...

  8. 邮件发送 --- 纯Java、JavaWeb、Spring三种实现方式

    邮件发送学习笔记~ 一.邮件发送介绍 要在网络上实现邮件功能,必须要有专门的邮件服务器! 这些邮件服务器类似于现实生活中的邮局,它主要负责接收用户投递过来的邮件,并把邮件投递到邮件接收者的电子邮箱中! ...

  9. 使用UTL_MAIL包实现存储过程邮件发送(转)

    邮件通知预警和提示在当前系统中已经是一个比较常见的功能.各类型语言分别针对邮件提供了功能包和API接口方法,本篇介绍如何在PL/SQL代码中使用UTL_MAIL工具包发送邮件,同时还介绍配置中注意的细 ...

  10. Java Mail 邮件发送(一):入门Demo

    本文首发于简书,Java Mail 邮件发送(一):Demo 上周公司的项目要求开发邮件发送功能.自己在网上跟着教程边学边做了一下午,现在基本开发完成了.由于一个同事也想看下该怎么写,顺便学习下.所以 ...

最新文章

  1. 我的世界java版怎么加整合包_我的世界1.9MOD简单整合下载 功能性懒人包
  2. python 发布到linux_Linux(CentOS)下的Python3部署流程
  3. springcloud 组件_深入理解 Spring Cloud 核心组件与底层原理
  4. JavaScript|拖拽|仿Android手机九点连线开锁
  5. clion variable set
  6. 如何让其他机器访问你的oracle数据库
  7. win7系统文件夹属性在哪打开
  8. 计算机网络之应用层:4、电子邮件、SMTP、MIME、POP3
  9. 脚本_批量修改md为hexo标准post格式[博]
  10. html实现在线展示pdf文件,在html页面中展示pdf文件,实现在线阅读
  11. 微信/qq/防撤回插件
  12. 迅雷下载Android Studio最新版本(Android Studio 2.1.2.0)
  13. 松翰单片机 c语言例程 宏定义出错,求助.松翰单片机 2714 用仿真器可以 , 但是烧到单片机不行. (amobbs.com 阿莫电子论坛)...
  14. realtek没有禁用前面板_为什么HD声卡必须禁用前面板插孔检测前置耳机和麦克才可以有声...
  15. linux perl 升级,科学网—一次Perl版本升级引发的吐槽大会 - 黄健的博文
  16. 2022.1.17学习总结
  17. 基于STM8L15x-16x-05x系列单片机的官方标准库文件创建第一个EWSTM8工程项目
  18. OV7670手册相关摘要
  19. 烈焰手游服务器维护,烈焰手游7月20日-7月24日合服安排公告
  20. 在word中输入带方块的对号

热门文章

  1. SAM4E单片机之旅——22、GMAC和PHY的介绍与初始化
  2. javascript中模仿接口(interface)
  3. 基于强化学习和析取图模型的统一调度框架
  4. 车间调度建模系列1|复杂车间调度问题特点
  5. leetcode刷题日记-三个无重叠子数组的最大和
  6. 数据结构:最小生成树
  7. 模板题——质数、素数、约数
  8. 夜间灯光数据下载(DMSP/OLS,NPP/VIIRS、珞珈一号网址)
  9. electron 自定义标题栏_electron+vue制作桌面应用--自定义标题栏
  10. java从入门到入土_java从入门到入土---基础篇04---IO