服務器的搭建我就不作過多的敘述了,網上有不少,這裏主要講一下SMTP的工做原理和工做過程。html
SMTP一般有兩種工做模式:發送SMTP和接收SMTP。具體工做方式爲:發送SMTP在接到用戶的郵件請求後,判斷此郵件是否爲本地郵件,如果直接投送到用戶的郵箱,不然向dns查詢遠端郵件服務器的MX紀錄,並創建與遠端接收SMTP之間的一個雙向傳送通道,此後SMTP命令由發送SMTP發出,由接收SMTP接收,而應答則反方面傳送。一旦傳送通道創建,SMTP發送者發送MAIL命令指明郵件發送者。若是SMTP接收者能夠接收郵件則返回OK應答。SMTP發送者再發出RCPT命令確認郵件是否接收到。若是SMTP接收者接收,則返回OK應答;若是不能接收到,則發出拒絕接收應答(但不停止整個郵件操做),雙方將如此重複屢次。當接收者收到所有郵件後會接收到特別的序列,若是接收者成功處理了郵件,則返回OK應答便可。sql
下面是我我的對郵件在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日誌記錄命令行/ide
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文件是郵件的實際內容;spa
.BDP是無用信息的回集;命令行
.BDR文件顯示了郵件沒有被髮送的緣由