蛙蛙推荐:写程序给IIS的SMTP服务加上邮件组功能
2008-09-17 20:38
225 查看
蛙蛙推荐:写程序给IIS的SMTP服务加上邮件组功能
摘要:windows自带的smtp服务不支持mail group功能,难道只能用exchange了吗?不,我们可以编程实现。昨天发了一个配置邮件服务器的帖子,被DUDU老大移到其它技术区了,这个应该不会被移除首页了吧。
上回文和大家分享了用windows2003自带的服务架设一个完整的邮件服务器的过程,但是作为一个企业,肯定有不同的组织,我们希望发送给Developer@fetionmm.com的邮件,所有开发人员都能收到,发给Tester@fetionmm.com的邮件,所有测试人员都能收到,这本是ex的Features,但今天我们来自己实现。
思路:比如我们新建了ms-onlytiancai的域,那么所有的用户的收件箱的邮件都默认放到C:/Inetpub/mailroot/Mailbox/ms-onlytiancai/目录的用户名子目录下,我们新建一个all用户作为一个邮件组,然后用c#的FileSystemWatch监控这个用户的收件箱目录是否有新邮件,如果有新邮件那么复制到所有其他用户的收件箱目录下,基本原理就是这些。其它就是把邮件组的成员设置成可配置的,在拷贝邮件的时候考虑邮件被其它进程占用的情况的容错和补偿处理。
没看上篇帖子的,先看上篇帖子
蛙蛙推荐:自己架设一个邮件服务器
http://www.cnblogs.com/onlytiancai/archive/2008/09/16/1292089.html
通过本文你可以学习到如下:
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
Groups#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性能会查一些,你可以可以考虑用win32 api来判断文件状态,再做个循环检查直到可以复制,当然是有这样的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为local system,选中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能否收到
摘要:windows自带的smtp服务不支持mail group功能,难道只能用exchange了吗?不,我们可以编程实现。昨天发了一个配置邮件服务器的帖子,被DUDU老大移到其它技术区了,这个应该不会被移除首页了吧。
上回文和大家分享了用windows2003自带的服务架设一个完整的邮件服务器的过程,但是作为一个企业,肯定有不同的组织,我们希望发送给Developer@fetionmm.com的邮件,所有开发人员都能收到,发给Tester@fetionmm.com的邮件,所有测试人员都能收到,这本是ex的Features,但今天我们来自己实现。
思路:比如我们新建了ms-onlytiancai的域,那么所有的用户的收件箱的邮件都默认放到C:/Inetpub/mailroot/Mailbox/ms-onlytiancai/目录的用户名子目录下,我们新建一个all用户作为一个邮件组,然后用c#的FileSystemWatch监控这个用户的收件箱目录是否有新邮件,如果有新邮件那么复制到所有其他用户的收件箱目录下,基本原理就是这些。其它就是把邮件组的成员设置成可配置的,在拷贝邮件的时候考虑邮件被其它进程占用的情况的容错和补偿处理。
没看上篇帖子的,先看上篇帖子
蛙蛙推荐:自己架设一个邮件服务器
http://www.cnblogs.com/onlytiancai/archive/2008/09/16/1292089.html
通过本文你可以学习到如下:
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
Groups#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性能会查一些,你可以可以考虑用win32 api来判断文件状态,再做个循环检查直到可以复制,当然是有这样的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为local system,选中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能否收到
相关文章推荐
- 蛙蛙推荐:写程序给IIS的SMTP服务加上邮件组功能
- Windows Server 2012 R2中通过IIS实现AD帐号密码修改功能 推荐
- 蛙蛙推荐:BS结构程序开发规范.doc
- 蛙蛙推荐:如何编写高质量的python程序
- windows 2008 64位oracle11g部署问题(3)之ORA-12514:TNS:监听程序当前无法识别连接描述符中请求的服务 推荐
- [日推荐]『饿了么外卖服务』饿了么官方小程序,无需下载安装!
- 蛙蛙推荐: 用web服务传递Dataset的一个简单例子
- windows2008 R2 角色 服务 功能 iis组件 名称
- 打造一个属于自己的应用服务自动监控警报程序 推荐
- ASP.NET C# 如何在程序中控制IIS服务或应用程序池重启?
- IIS 子验证程序的注册表键没有在本地计算机上正确配置,匿名密码同步功能被禁用
- 【蛙蛙推荐】创建一个数据字典服务
- 关于IIS服务启动失败的问题:“IIS提示‘另一个程序正在使用此文件,进程无法访问’”,的解决方法
- windows2003 IIS 服务启动失败,提示‘另一个程序正在使用此文件,进程无法访问',解决方法
- 蛙蛙推荐:C语言入门之二——编写第一个有意义的小程序
- 蛙蛙推荐:设计一个高效的缓存管理服务
- 蛙蛙推荐:刚刚写了一个考试程序,给大家贴一下,我没写过多注释哦
- Windows server 2012 新功能试用---- powershell 3.0 进程和服务的操作 推荐
- 蛙蛙推荐:利用操作符重载提高程序的可读性
- iis服务下vs2005的web程序无法启动