SMTP服务器
服务器的搭建我就不做过多的叙述了,网上有很多,这里主要讲一下SMTP的工作原理和工作过程。
SMTP通常有两种工作模式:发送SMTP和接收SMTP。具体工作方式为:发送SMTP在接到用户的邮件请求后,判断此邮件是否为本地邮件,若是直接投送到用户的邮箱,否则向dns查询远端邮件服务器的MX纪录,并建立与远端接收SMTP之间的一个双向传送通道,此后SMTP命令由发送SMTP发出,由接收SMTP接收,而应答则反方面传送。一旦传送通道建立,SMTP发送者发送MAIL命令指明邮件发送者。如果SMTP接收者可以接收邮件则返回OK应答。SMTP发送者再发出RCPT命令确认邮件是否接收到。如果SMTP接收者接收,则返回OK应答;如果不能接收到,则发出拒绝接收应答(但不中止整个邮件操作),双方将如此重复多次。当接收者收到全部邮件后会接收到特别的序列,如果接收者成功处理了邮件,则返回OK应答即可。
下面是我个人对邮件在SMTP服务器文件夹中的旅行路径,如有错误请指正:
邮件发送分为同步发送和异步发送:

1 /// <summary> 2 /// 邮件设置 3 /// </summary> 4 /// <param name="toAddress">收件人地址</param> 5 /// <param name="fromAddress">发件人地址</param> 6 /// <param name="toName">收件人名字</param> 7 /// <param name="fromName">发件人姓名</param> 8 /// <param name="title">主题</param> 9 /// <param name="body">正文</param> 10 /// <param name="isBodyHtml">正文是否为html格式</param> 11 public SendEmail(string toAddress, string fromAddress, string toName, string fromName, string title, string body, bool isBodyHtml) 12 { 13 mailMessage.From = new MailAddress(fromAddress, fromName, Encoding.GetEncoding(936)); 14 if (toName.Equals("")) 15 mailMessage.To.Add(toAddress); 16 else 17 mailMessage.To.Add(new MailAddress(toAddress, toName, Encoding.GetEncoding(936))); 18 19 mailMessage.Subject = title; 20 mailMessage.SubjectEncoding = Encoding.GetEncoding(936); 21 22 mailMessage.Body = body; 23 mailMessage.IsBodyHtml = isBodyHtml; 24 mailMessage.BodyEncoding = Encoding.GetEncoding(936); 25 } 26 /// <summary> 27 /// 设置SMTP,并且将邮件发送出去 28 /// 所有参数都设置完成后再调用该方法 29 /// </summary> 30 /// <param name="address">发件人地址(必须为真实有效的email地址)</param> 31 /// <param name="password">发件人密码</param> 32 /// <param name="smtpHost">SMTP服务器地址,例如:smtp.163.com</param> 33 /// <param name="smtpPort">SMTP服务器的端口,一般为25</param> 34 /// <param name="isEnableSsl">SMTP服务器是否启用SSL加密</param> 35 /// <param name="priority">邮件的优先级</param> 36 /// <param name="UseDefaultCredentials">设置是否需要身份验证</param> 37 public void SetSmtp(string address, string password, string smtpHost, int smtpPort, bool isEnableSsl, MailPriority priority) 38 { 39 var smtp = new SmtpClient 40 { 41 DeliveryMethod = SmtpDeliveryMethod.Network,//电子邮件通过网络发送到SMTP服务器 42 Credentials = new NetworkCredential(address, password), 43 Host = smtpHost, 44 Port = smtpPort, 45 UseDefaultCredentials = true, 46 EnableSsl = isEnableSsl 47 }; 48 mailMessage.Priority = priority; 49 //smtp.Send(mailMessage); //同步发送邮件 50 smtp.SendAsync(mailMessage, mailMessage.To.ToString());//异步发送邮件 51 }
除非检测SMTP的log日志文件,否则我们并不知道邮件是否发送成功,检测SMTP日志可以用logparser工具,他可以像sql那样通过查询语句来获得log的日志内容:
net start smtpsvc /启动smtp日志记录命令行/

1 using LogQuery = MSUtil.LogQueryClass; 2 using LogRecordSet = MSUtil.ILogRecordset; 3 using TsvInputFormat = MSUtil.COMTSVInputContextClass; 4 public class LogParser 5 { 6 /// <summary> 7 /// 计算日志条数 8 /// </summary> 9 public double GetLogCount(string headerFile, string logName) 10 { 11 double avgLockSec = 0; 12 var oLogQuery = new LogQuery(); 13 var oTsvInputFormat = new TsvInputFormat { iHeaderFile = headerFile }; 14 string query = "select * from '" + logName + "'"; 15 LogRecordSet oRecordSet = oLogQuery.Execute(query, oTsvInputFormat); 16 if (!oRecordSet.atEnd()) 17 { 18 double item = 0; 19 if (!oRecordSet.getRecord().isNull(0)) 20 { 21 item = double.Parse(oRecordSet.getRecord().getValue(0).ToString()); 22 } 23 avgLockSec = item; 24 } 25 oRecordSet.close(); 26 return avgLockSec; 27 } 28 }
根据上面图片我们知道,发送失败的Email会放到Drop和BadEmail两个文件夹中,所以只要监测两个文件夹,有文件进来就是发送失败的邮件:

1 public static class MyFileSystemWatcher 2 { 3 [PermissionSet(SecurityAction.Demand, Name = "FullTrust")] 4 public static void WatcherStrat(string path) 5 { 6 var watcher = new FileSystemWatcher { Path = path, Filter = "*.*" }; 7 watcher.Created += new FileSystemEventHandler(OnProcess); 8 watcher.EnableRaisingEvents = true; 9 watcher.NotifyFilter = NotifyFilters.Attributes | NotifyFilters.CreationTime | NotifyFilters.DirectoryName | NotifyFilters.FileName | NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.Security | NotifyFilters.Size; 10 watcher.IncludeSubdirectories = true; 11 } 12 public static void OnProcess(object source, FileSystemEventArgs e) 13 { 14 switch (e.ChangeType) 15 { 16 case WatcherChangeTypes.Created: 17 OnCreated(source, e); 18 break; 19 } 20 } 21 public static void OnCreated(object source, FileSystemEventArgs e) 22 { 23 Console.WriteLine("文件改变事件{0}文件路径{1}文件名{2}", e.ChangeType, e.FullPath, e.Name); 24 } 25 public static void OnChanged(object source, FileSystemEventArgs e) 26 { 27 Console.WriteLine("文件改变事件{0}文件路径{1}文件名{2}", e.ChangeType, e.FullPath, e.Name); 28 } 29 public static void OnDeleted(object source, FileSystemEventArgs e) 30 { 31 Console.WriteLine("文件删除事件{0}文件路径{1}文件名{2}", e.ChangeType, e.FullPath, e.Name); 32 } 33 public static void OnRenamed(object source, RenamedEventArgs e) 34 { 35 Console.WriteLine("文件重命名事件{0}文件路径{1}文件名{2}", e.ChangeType, e.FullPath, e.Name); 36 } 37 public static void GetFilesSystemRights(string path) 38 { 39 //给文件添加"Everyone,Users"用户组的完全控制权限 40 var fi = new FileInfo(path); 41 System.Security.AccessControl.FileSecurity fileSecurity = fi.GetAccessControl(); 42 fileSecurity.AddAccessRule(new FileSystemAccessRule("Everyone", FileSystemRights.FullControl, AccessControlType.Allow)); 43 fileSecurity.AddAccessRule(new FileSystemAccessRule("Users", FileSystemRights.FullControl, AccessControlType.Allow)); 44 fi.SetAccessControl(fileSecurity); 45 //给文件所在目录添加"Everyone,Users"用户组的完全控制权限 46 var di = new DirectoryInfo(path); 47 System.Security.AccessControl.DirectorySecurity dirSecurity = di.GetAccessControl(); 48 dirSecurity.AddAccessRule(new FileSystemAccessRule("Everyone", FileSystemRights.FullControl, AccessControlType.Allow)); 49 dirSecurity.AddAccessRule(new FileSystemAccessRule("Users", FileSystemRights.FullControl, AccessControlType.Allow)); 50 di.SetAccessControl(dirSecurity); 51 52 } 53 }
在Global.asax文件中添加MyFileSystemWatcher.WatcherStrat("文件夹路径");以便在服务启动时就开始监测文件夹。
.BAD文件是邮件的实际内容;
.BDP是无用信息的回集;
.BDR文件显示了邮件没有被发送的原因