设为首页收藏本站

LUPA开源社区

 找回密码
 注册
文章 帖子 博客
LUPA开源社区 首页 业界资讯 技术文摘 查看内容

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

2013-9-3 15:01| 发布者: 红黑魂| 查看: 12227| 评论: 0|来自: 博客园

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

我开发的一个发送邮件的小组件(代码在博文开始处已给出下载地址)

         为了简化邮件发送代码编写和SmtpClient实例的管理,我封装了一个发邮件的帮助类。

这个帮助类,包含如图几个文件:

 

两个主要类: SmtpHelper MailHelper

1.         SmtpHelper     

此类是为了简化构造SmtpClient实例所需的代码量。通过SmtpHelper构造函数设置好SMTP服务器、端口号、身份凭据,再通过链式操作快速设置SmtpClient其他不常使用的属性。

Eg

1
2
3
SmtpClient client = new SmtpHelper( host, port, false, userName, password)
                   .SetTimeout(60*1000)
                   .SmtpClient;

使用SmtpHelper类注意事项:

1)         非线程安全类.

2)         构造的SmtpClient 实例外部进行Dispose()SmtpHelper类只简单提供构造,不做释放操作。

3)         SmtpClient 没有提供 Finalize() 终结器,所以GC不会进行回收,只能由外部使用完后进行显示释放,否则会发生内存泄露问题.

 

2.         MailHelper

此类完成邮件的发送工作。需要结合MailInfoHelper静态类验证邮件信息的有效性。

1)         支持快捷添加附件、内嵌资源、地址信息、备用视图格式;

Eg:添加内嵌资源

1
2
3
// 邮件内容:"点击在新窗口打开图片";
string picPath = Environment.CurrentDirectory + "\\附件\\PIC_Mail中文.png";
mail.AddInlineAttachment(picPath,"MyPic");

2)         支持在发送邮件前对邮件信息有效性进行检查;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Dictionarystring> dic = mail.CheckSendMail();
if (dic.Count > 0 && MailInfoHelper.ExistsError(dic))
{
    // 反馈“错误+提示”信息
    msg = MailInfoHelper.GetMailInfoStr(dic))
}
else
{
    if (dic.Count > 0)
    {
        // 反馈“提示”信息,但还是可以发送邮件
        msg = MailInfoHelper.GetMailInfoStr(dic);
    }
   
    // 发送邮件
}

3)         支持批量同步、异步发送邮件

a)         批量同步发送邮件:实际上只是 SmtpClient.Send() 同步发送邮件的一个封装。

b)         批量异步发送邮件

                                       i.              待发送队列:因为一个SmtpClient一次只能发送一个MailMessage,不管是同步还是异步发送,所以 SmtpClient.SendAsync() 方法后必须阻塞线程直到上一封邮件发送完成,否则会抛出“正在发送邮件”的异常。所以,MailHelper为了避免调用线程的阻塞,将待发送邮件的信息都加入到队列中,内部启用一个线程去执行串行化发送任务。

                                      ii.              限流:“异步”批量发送过程中,为了防止待发送队列无限制的增大,导致内存溢出,我们可以通过MailHelperGetAwaitMailCountAsync()方法监控该队列的大小,适当的执行Thread.Sleep(time).

                                    iii.              异步取消:可以通过MailHelperSendAsyncCancel()方法,取消待发送队列中的邮件继续发送。

                                    iv.              回调函数:异步发送完一封电子邮件后执行的回调函数。通过SendCompleted事件进行注册。但要注意其AsyncCompletedEventArgs参数的UserState对象被改写为了我定义的 MailUserState 对象 

MailUserState定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
///
/// 异步发送邮件时保存的信息,用于释放和传递数据
///
public class MailUserState
{
    #region 由MailHelper内部的SendCompleted注册的事件使用
    // 用于释放 MailMessage 和 SmtpClient
    public MailMessage CurMailMessage { get; set; }
    public bool AutoReleaseSmtp { get; set; }
    public SmtpClient CurSmtpClient { get; set; }
    // 只发送单封邮件的时候使用此进行判断释放 
    public bool IsSmpleMail { get; set; }
    #endregion
 
    ///
    /// 用户传递的状态对象
    ///
    public object UserState { get; set; }
 
    ///
    /// 当异步发送报错时可通过此标识是否已经处理该异常
    ///
    public bool IsErrorHandle { get; set; }
}

 

批量异步发送示例(注意回调函数的用户信息):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 设置SmtpClient的回调函数
client.SendCompleted +=(send,args) =>
{
    AsyncCompletedEventArgs arg = args;
    MailUserState userState = arg.UserState as MailUserState;
}
// 在MailHelper的构造函数中决定是同步发送还是异步发送邮件
MailHelper mail = new MailHelper(client,true,isAsync);
for (long i = 1; i <= mailCount; i++)
{
    if(mail.GetAwaitMailCountAsync()>1000)
    {
        // 当待发送队列大于1000时,线程休眠1秒
        Thread.Sleep(1000);
    }
        // 设置 MailHelper 发送信息
        // ……
        // 设置每封电子邮件发送完执行回调函数的UserState
        mail.AsyncUserState = “你传递的对象信息”;
        // 执行批量发送邮件
        mail.SendBatchMail();
}
mail.SetBatchMailCount(count);

 

4)         批量发送邮件中,每调用一次发送方法,要使用MailHelperReset()对邮件内容进行重置。

注意:

a)         不重置SmtpClientSmtpClient根据 m_autoDisposeSmtp 参数自动释放或由外部主动释放

b)         不重置:异步待发送队列及队列计数、AutoResetEvent实例、执行异步发送线程变量、是否启用异步发送标识变量

5)         支持自动释放SmtpClient实例

在平常邮件开发中,当在异步批量发送邮件时,我们没办法掌握何时释放我们重用的SmtpClient实例。

但,我们使用MailHelper类,可以不用关心SmtpClient的释放问题。我们通过构造函数中指定自动释放SmtpClient的参数为true,并且统计好批量邮件发送量之后调用 SetBatchMailCount(long preCount) 方法,MailHelper就会在(批量)同步、(批量)异步邮件全部发送完之后自动释放SmtpClient实例。

a)         为什么要“重用”同一个SmtpClient实例

因为,每次发送一封电子邮件,都必须经过TCP的三次握手与服务器建立连接,这个连接信息就保存在SmtpClient实例中,所以当进行大批量的电子邮件发送时(前提是发件地址是相同的,当然大部分场景下发件地址都是相同的),有必要重用SmtpClient实例,避免TCP不断地发生“三次握手和四次挥手”。

b)         为什么要“显示释放”SmtpClient实例

SmtpClient类没有 Finalize (终结器)方法,因此应用程序"必须"调用 Dispose 来显式释放资源。 Dispose  方法在所有建立到 Host 属性中指定的 SMTP 服务器的连接中循环,并发送 QUIT 消息,其后平稳断开 TCP 连接。

 

MailHelper组件的一个示例以及几种方式发邮件的优劣测试

         示例(博文开始处已给出链接下载)包含四次实验方案和两组复选框,如图:

 

 

示例代码下载后注意,请先修改如Config.cs文件的几处红色标识信息(如下图),你才能正常发送邮件。


 

QQ邮箱发件的注意啦,要在“设置”-“账户”中将“POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务”服务都开启才能正常发送邮件。如图:


 

实验一:单条邮件同步和异步发送(可通过添加大附件来观察同步异步效果)

观察:通过大附件观察下同步发送邮件和异步发送邮件的效果,查看下单封邮件发送MailHelper类是如何使用的。

 

实验二:批量邮件同步和异步发送(单个线程,单个SmtpClient实例,SendAsync()

观察:观察下MailHelper类中批量异步发送使用队列方式实现的高响应性,以及批量操作如何自动释放SmtpClient实例。

 

在数量较大的批量邮件发送场景中,我们可以使用多个SmtpClient实例来并行发送,以提高整体发件效率。即实验三 + 实验四(不清楚并行类库的,请看 异步编程:.NET4.X 数据并行 

 

实验三:批量邮件同步和异步发送(平行类库Parallel(自动分区),每个分区一个MailHelperSmtpClient实例)

观察:现在通过Parallel.For的自动分区 + 每个分区一个MailHelper SmtpClient实例来提高整体效率。但是,有个问题就是自动分区又.NET内部根据资源负载均衡自动分区,分区的效果非常不好,总会开启过多的分区导致MailHelperSmtpClient实例偏多,并且效率不高。

 

实验四:批量邮件同步和异步发送(平行类库Parallel(手动分区),每个分区一个MailHelperSmtpClient实例)

观察:在通过Parallel.Foreach的手动分区 + 每个分区一个MailHelper SmtpClient实例来提高整体效率。我们自己根据业务场景和Environment.ProcessorCount内核数来决定分区数,这样可以根据需要创建MailHelperSmtpClient实例,并且效率非常高。

 

另外:重用SmtpClient复选款的测试结果:如果只是简单的纯文本邮件发送(即,没有耗时的附件内容),重用SmtpClient可提升50%的效率。(注意:需要使用批量同步方式发送进行测试。因为异步方式会使用多个SmtpClient进行并行发送所以测试不出效率提升)

 

来个整个示例截图:


 

 

 

本邮件发送功能分享到此结束,如果你看后觉得对你有帮助的,还请多帮推荐……推荐……如果内容有误的,还请帮忙指出,谢谢!


http://www.cnblogs.com/heyuquan/p/net-batch-mail-send-async.html


酷毙

雷人

鲜花

鸡蛋

漂亮
  • 快毕业了,没工作经验,
    找份工作好难啊?
    赶紧去人才芯片公司磨练吧!!

最新评论

关于LUPA|人才芯片工程|人才招聘|LUPA认证|LUPA教育|LUPA开源社区 ( 浙B2-20090187 浙公网安备 33010602006705号   

返回顶部