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 }
SendEmail
除非检测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 }
LogParser
根据上面图片我们知道,发送失败的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文件显示了邮件没有被发送的原因