邮件发送需考虑很多因素,包括发送邮件客户端(一般编码实现),发送和接收邮件服务器设置等。如果使用第三方邮件服务器作为发送服务器,就需要考虑该服务器的发送限制,(如发送邮件时间间隔,单位时间内发送邮件数量,是否使用安全连接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);
else
throw 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);
else
throw 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 body
var 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, -);
}
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 = ;//Need to be reset to 0, otherwise nothing will be sent;
Host.SendMessage(stream);
Host.Disconnect();
}
}
else
throw 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 = ,
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
mx.google.com ESMTP o5sm40420786eeg. - gsmtp
EHLO g1
-mx.google.com at your service, [173.231.8.212]
-SIZE
-8BITMIME
-STARTTLS
-ENHANCEDSTATUSCODES
CHUNKING
AUTH LOGIN
5.7. Must issue a STARTTLS command first. o5sm40420786eeg. – gsmtpSTARTTLS220
STARTTLS
2.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 = ,
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#邮件发送问题(二)

C#邮件发送问题(一)的更多相关文章

  1. .NET开发邮件发送功能的全面教程(含邮件组件源码)

    今天,给大家分享的是如何在.NET平台中开发“邮件发送”功能.在网上搜的到的各种资料一般都介绍的比较简单,那今天我想比较细的整理介绍下: 1)         邮件基础理论知识 2)         ...

  2. J2EE 邮件发送那些事儿

    距离自己写的关于java邮件发送的第一篇博客已经有很长一段时间了,现在回过头看看.虽然代码质量方面有待提高,整体结构也不怎样,但是基本思路和过程还是比较纯的.现在有空写写J2EE中邮件发送的开发,实际 ...

  3. 结合ABP源码实现邮件发送功能

    1. 前言 2. 实现过程 1. 代码图(重) 2.具体实现 2.1 定义AppSettingNames及AppSettingProvider 2.2 EmailSenderConfiguration ...

  4. SSH项目里面 忘记密码的邮件发送功能

    package com.xxx.util; import java.util.Date; import java.util.Properties; import javax.mail.Address; ...

  5. [UWP]UWP中获取联系人/邮件发送/SMS消息发送操作

    这篇博客将介绍如何在UWP程序中获取联系人/邮件发送/SMS发送的基础操作. 1. 获取联系人 UWP中联系人获取需要引入Windows.ApplicationModel.Contacts名称空间. ...

  6. java spring 邮件发送

    开发中经常会遇到发送邮件进行用户验证,或者其它推送信息的情况,本文基于spring,完成邮件的发送,主要支持普通文本邮件的发送,html文本邮件的发送,带附件的邮件发送,没有实现群发.多个附件发送等需 ...

  7. Java邮件发送与接收原理

    一. 邮件开发涉及到的一些基本概念 1.1.邮件服务器和电子邮箱 要在Internet上提供电子邮件功能,必须有专门的电子邮件服务器.例如现在Internet很多提供邮件服务的厂商:sina.sohu ...

  8. c#实现邮件发送链接激活

    2016-08-24 10:09:52 public void MailSend(string email) { MailMessage MyMail = new MailMessage(); MyM ...

  9. .Net(C#)最简单的邮件发送案例

    一.序言 刚开始接触邮件发送功能的时候,在网上找的资料都挺复杂的!对于新手入门有点难(至少对于本人来说,第一次接触的时候确实不容易).这里就写一段简单的邮箱发送代码,备忘,也给新手一个参考(相关类的字 ...

  10. SpringMVC 邮件发送

    <!--邮件发送实现类--> <bean id="javaMailSender" class="org.springframework.mail.jav ...

随机推荐

  1. JavaScript的DOM操作。Window.document对象

    间隔执行一段代码:window.setlnteval("需要执行的代码",间隔毫秒数) 例 :      window.setlnteval("alert("你 ...

  2. 每天一命令 git stash

    git stash  命令是用于保存当前进度的命令.该命令会保存当前工作区的改动.保存的改动是已经跟踪的文件的改动,对于未跟踪的改动stash是不会保存的. git stash 命令常用于分支切换的 ...

  3. 超酷HTML5 Canvas图表应用Chart.js自定义提示折线图

    超酷HTML5 Canvas图表应用Chart.js自定义提示折线图 效果预览 实例代码 <div class="htmleaf-container"> <div ...

  4. 微信网页授权(OAuth2.0) PHP 源码简单实现

    提要: 1. 建议对OAuth2.0协议做一个学习. 2. 微信官方文档和微信官网工具要得到充分利用. 比较简单,直接帖源代码了.其中“xxxxxxxxxx”部分,是需要依据自己环境做替换的 /** ...

  5. 强大的JavaScript动画图形库mo.js

    最近在学习前端动画方面知识时发现了挺有趣的一个动画的图形库mo.js,页面效果真是酷炫,有兴趣的同学可以研究下:). 酷炫的效果: 以下是官方的demo效果,更多详情请查看 mo.js http:// ...

  6. SharePoint Online 创建门户网站系列之母版页

    前 言 虽然SharePoint中母版页看起来只是头部Banner和底部版权信息这两个部分,但是实质在SharePoint Online中的页面模型中占有重要地位,而且SPO对母版页有着完善的签入签出 ...

  7. 根据werservice代码用CXF生成WSDL

    原文:http://hongyegu.iteye.com/blog/619147,谢谢! import org.apache.cxf.tools.java2ws.JavaToWS; import ne ...

  8. clang编译mysql(Ubuntu10 64位)

    编译安装mysql(Ubuntu10 64位) http://www.cnblogs.com/2018/p/3482259.html 这个介绍了gcc形式编译安装mysql 那我们使用clang进行编 ...

  9. 动画效果 View控件的显示和隐藏效果

    显示动画: mShowAction = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 1.0f,     Animation.RELATIVE_ ...

  10. 面试题:“你能不能谈谈,java GC是在什么时候,对什么东西,做了什么事情?”

    面试题目:地球人都知道,Java有个东西叫垃圾收集器,它让创建的对象不需要像c/cpp那样delete.free掉,你能不能谈谈: GC是在什么时候,对什么东西,做了什么事情? 以上算是三个问题,下面 ...