Asp.net给IIS的SMTP服务加上邮件组功能

通过本文你可以学习到如下:
1、.net 2.0的自定义配置
2、拷贝文件如何折中考虑多进程并发问题
3、怎样最简单的制作windows服务
4、怎样让IIS自带的smtp服务支持mail group功能先定义邮件组配置,如下
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="MailGroup" type="WawaSoft.MailGroup.Config,MailGroupLib"/>
  </configSections>
  <MailGroup>
    <Groups>
      <add Dir="C:\Inetpub\mailroot\Mailbox\ms-onlytiancai\P3_all.mbx">
        <Members>
          <add Dir="C:\Inetpub\mailroot\Mailbox\ms-onlytiancai\P3_huhao.mbx"></add>
        </Members>
      </add>
    </Groups>
  </MailGroup>
</configuration>
其中Groups\add是可重复的,意思就是可以建立多个组,Members\add也是可重复的,意思是一个组可以有多个成员,dir属性就是邮件组或成员的收件箱目录,这段自定义配置的处理代码如下

using System.Configuration;



namespace WawaSoft.MailGroup
{
    public class Config : ConfigurationSection
    {

        #region 单件实现
        private static Config _config;
        public static Config Instance
        {
            get
            {
                if (_config == null)
                {
                    lock (typeof (Config))
                    {
                        if (_config == null)
                            _config = ConfigurationManager.GetSection("MailGroup") as Config;
                    }
                }
                return _config;
            }
        }

        #endregion

        MailGroup#region MailGroup

        [ConfigurationProperty("Groups")]
        internal GroupCollection Groups
        {
            get { return (GroupCollection) this["Groups"]; }
        }

        #endregion

        #region Groups

        internal class GroupCollection : ConfigurationElementCollection
        {

            public Group this[int index]
            {
                get { return (Group) BaseGet(index); }
            }

            public new Group this[string dir]
            {
                get { return (Group) BaseGet(dir); }
            }

            public override ConfigurationElementCollectionType CollectionType
            {
                get { return ConfigurationElementCollectionType.AddRemoveClearMap; }
            }

            protected override ConfigurationElement CreateNewElement()
            {
                return new Group();
            }

            protected override object GetElementKey(ConfigurationElement element)
            {
                return ((Group) element).Dir;
            }
        }

        #endregion

        Group#region Group
        internal class Group : ConfigurationElement
        {
            [ConfigurationProperty("Dir")]
            public string Dir
            {
                get { return (string) this["Dir"]; }
            }

            [ConfigurationProperty("Members")]
            public MemberCollection Members
            {
                get { return (MemberCollection) this["Members"]; }
            }
        }

        #endregion

        Members#region Members
        internal class MemberCollection : ConfigurationElementCollection
        {
            public Member this[int index]
            {
                get { return (Member) BaseGet(index); }
            }

            public new Member this[string dir]
            {
                get { return (Member) BaseGet(dir); }
            }

            protected override ConfigurationElement CreateNewElement()
            {
                return new Member();
            }

            protected override object GetElementKey(ConfigurationElement element)
            {
                return ((Member) element).Dir;
            }
        }

        #endregion
        Member#region Member
        internal class Member : ConfigurationElement
        {
            [ConfigurationProperty("Dir")]
            public string Dir
            {
                get { return (string) this["Dir"]; }
            }
        }
        #endregion
    }
}




然后就是读取配置,监控邮件组收件箱,复制到成员收件箱,如下

using System;
using System.IO;
using WawaSoft.Common;
using System.Threading;


namespace WawaSoft.MailGroup
{
    public class MailWatcher
    {
        private static readonly ITracer _tracer = TracerFactory.GetTrace(typeof (MailWatcher));

        public static void Start()
        {
            _tracer.Info("启动成功");
            Config.GroupCollection collection = Config.Instance.Groups;
            foreach (Config.Group group in collection)
            {
                if (!Directory.Exists(group.Dir))
                {
                    _tracer.Info("{0}不存在\r\n", group.Dir);
                    continue;
                }
                FileSystemWatcher fileSystemWatcher =
                    new FileSystemWatcher(group.Dir, "*.eml");
                _tracer.Info("监听{0}", group.Dir);
                Config.Group group1 = group;
                fileSystemWatcher.Created +=
                    delegate(object sender, FileSystemEventArgs e)
                        {
                            try
                            {
                                Config.MemberCollection members = group1.Members;
                                foreach (Config.Member member in members)
                                {
                                    try
                                    {
                                        string target = string.Format("{0}\\{1}",
                                                                      member.Dir, e.Name);
                                        ReliableCopy(e.FullPath, target, 0);
                                        _tracer.Info("copy {0} to {1}",
                                                     e.FullPath, target);
                                    }
                                    catch (Exception ex)
                                    {
                                        _tracer.Error(ex, "复制邮件出错");
                                    }
                                }
                                File.Delete(e.FullPath);
                            }
                            catch (Exception ex2)
                            {
                               
                                _tracer.Error(ex2, "执行出错");
                            }
                        };
                fileSystemWatcher.EnableRaisingEvents = true;
            }
        }

        private static void ReliableCopy(string path, string target, int retryCount)
        {
            if(retryCount>0)
                _tracer.Warn("正在执行第{0}次重试:{1}",retryCount,target);
            if (retryCount > 5)
                throw new ApplicationException(string.Format(
                                                   "重试次数超过限制:{0}", target));
            try
            {
                File.Copy(path, target, true);
            }
            catch (IOException) //捕获IO异常
            {
                Thread.Sleep(5000);
                ReliableCopy(path,target, ++retryCount);
            }
            //其它异常直接抛给调用者
        }
    }
}




代 码很简单,但又时候邮件特别大的话,邮件组的收件箱接受邮件需要一定的时间,而文件刚创建就会触发FileSystemWatch的事件,就会开始尝试拷 贝复制文件,而要复制的文件,正在接受新的字节,写入到自身,这种情况下进行File.Copy会报该文件被其它进程使用,所以我们拷贝的时候如果遇到这 个错,可以重试5次,每次重试Sleep 5秒,这也是一个折中的办法,当然用try catch性能会查一些,你可以可以考虑用win32api来判断文件状态,再做个循环检查直到可以复制,当然是有这样的API的话。
然后创建一个Service类,如下

using System.ServiceProcess;



namespace WawaSoft.MailGroup

{

    internal partial class Service1 : ServiceBase

    {

        public Service1()

        {

            InitializeComponent();

        }



        protected override void OnStart(string[] args)

        {

            MailWatcher.Start();

        }

    }

}




并 在属性面板里给服务设置ServiceName属性,点这个服务属性下面的“添加安装程序”按钮,会添加一个ProjectInstaller.cs类, 选中serviceProcessInstaller1,设置Accout为localsystem,选中serviceInstaller1,设置 DispayName和ServiceName为MailGroupService,把StartType设置为automatic,就是自动启动就行 了,这样就是一个windows服务了。
安装的使用用以下命令安装服务
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\installutil MailGroupLib.exe
pause
最后在services.msc里启用服务就行了。
测试:用telnet给all@ms-onlytiancai发邮件,看huhao@ms-onlytiancai能否收到