今天,給大家分享的是如何在.NET平臺中開發“郵件發送”功能。在網上搜的到的各種資料一般都介紹的比較簡單,那今天我想比較細的整理介紹下:
1) 郵件基礎理論知識
2) 郵件發送相關.NET類庫
3) 介紹我開發的一個發送郵件的小組件(MailHelper)
4) MailHelper組件的一個示例以及幾種方式發郵件的優劣測試
示例及組件源碼:
.NET開發郵件發送功能的全面教程(含郵件組件源碼).rar
郵件基礎理論知識
什麼業務需要郵件功能?
1. 服務提供方:需提供郵件收發客戶端或Web服務。(eg:Outlook、QQ郵箱)。當然這些服務都是知名商提供。若是一般的小網站提供的郵件收發服務,不知道節操如何,誰敢用呢?就算你用了,別的知名商SMTP服務器也不認可從這小網站發出的郵件,出現SMTP服務器拒收來源郵件(視爲惡意郵件或垃圾郵件)。
2. 安全性、機密性:比如某安全部門需要提供自己發郵件的SMTP服務器和收郵件POP3服務器以及相應的操作軟件
3. 電子商務、論壇等會員機制社區:主家需要向會員發送通知信息,比如:密碼重置、降價通知、留言通知、回覆通知、訂閱通知、會員間交流等等。主家保證郵箱有效性的辦法常常是通過會員註冊、更換郵箱時發送“激活郵件”。
4. 郵件營銷:在大數據時代的現在,企業可以根據所掌握的數據預測客戶的需求,來提供主動推送營銷消息的功能;當然也有沒有預測能力的小商家通過郵件羣發器進行撒網式郵件營銷。
5. 等等
什麼是電子郵件協議?
當前常用的電子郵件協議有SMTP、POP3、IMAP4,它們都隸屬於TCP/IP協議簇。
1. SMTP
Simple Mail Transfer Protocol(即簡單郵件傳輸協議),它是一組用於從源地址到目的地址傳送郵件的規則,簡單的說就是:From-->To的傳送規則。由SMTP來控制信件中轉的方式。SMTP屬於TCP/IP家族中的一員,它幫助每一臺計算機在發送或中轉信件時找到下一個目的地。通過SMTP協議所指定的服務器,就可以把E-Mail寄到收信人的服務器上。SMTP服務器則是遵循SMTP協議的郵件發送服務器,用來中轉你發出的電子郵件。
SMTP目前已是事實上的E-Mail傳輸的標準。
2. POP3
Post Office Protocol 3(即郵局協議的第3個版本),負責從郵件服務器中檢索電子郵件。它要求郵件服務器完成下面幾種任務之一:從郵件服務器中檢索郵件並從服務器中刪除這個郵件;從郵件服務器中檢索郵件但不刪除它;不檢索郵件,只是詢問是否有新郵件到達。
POP3是因特網電子郵件的第一個離線協議標準。
3. IMAP4
Internet Message Access Protocol 4(即交互式數據消息訪問協議第四個版本),提供脫機和聯機訪問功能。是一種優於POP的新協議,是美國斯坦福大學在1986年開始研發的多重郵箱電子郵件系統。和POP一樣,IMAP也能下載郵件、從服務器中刪除郵件或詢問是否有新郵件,但IMAP克服了POP的一些缺點。例如,請求郵件服務器只下載所選中的郵件而不是全部郵件。客戶機可先閱讀郵件信息的標題和發送者的名字再決定是否下載這個郵件。通過用戶的客戶機電子郵件程序,IMAP可讓用戶在服務器上創建並管理郵件文件夾或郵箱、刪除郵件、查詢某封信的一部分或全部內容,完成所有這些工作時都不需要把郵件從服務器下載到用戶的個人計算機上。
默認情況下,當 IMAP4 電子郵件應用程序將電子郵件下載到客戶端計算機,下載郵件的副本會保留在電子郵件服務器上。正是由於用戶的電子郵件副本保留在電子郵件服務器上,用戶可以從多臺計算機上訪問相同的電子郵件。也可以實現電子郵件服務器上的多個文件夾與客戶端計算機上的多個文件夾同步。
SMTP/POP3工作方式如圖:
TCP的3次握手和4次揮手?
詳細可見《TCP3次握手/4次握手》
在 TCP 數據段報頭中,有六個包含控制信息的 1 bit字段,用於管理 TCP 進程。這些字段分別是:
URG —緊急指針
ACK —確認字段
PSH —推送功能
RST —重置連接
SYN —同步序列號
FIN —發送方已傳輸完所有數據
這些字段用作標誌,由於它們都只有 1 bit大小,所以它們都只有兩個值:1 或者 0。當值設爲 1 時,表示數據段中包含控制信息。
1. 三次握手,建立連接
在TCP/IP協議中,TCP協議提供可靠的連接服務,採用三次握手建立一個連接。
1) 建立連接時,客戶端A發送SYN包(SYN=j)到服務器B,並進入SYN_SEND狀態,等待服務器B確認。
2) 服務器B收到SYN包,必須確認客戶A的SYN(ACK=j+1),同時自己也發送一個SYN包(SYN=k),即SYN+ACK包,此時服務器B進入SYN_RECV狀態。
3) 客戶端A收到服務器B的SYN+ACK包,向服務器B發送確認包ACK(ACK=k+1),此包發送完畢,客戶端A和服務器B進入ESTABLISHED狀態,完成三次握手。
2. 四次揮手,關閉連接
由於TCP連接是全雙工的,因此每個方向都必須單獨進行關閉。這個原則是當一方完成它的數據發送任務後就能發送一個FIN來終止這個方向的連接。
1) 客戶端A發送一個FIN,用來關閉客戶A到服務器B的數據傳送。
2) 服務器B收到這個FIN,它發回一個ACK,確認序號爲收到的序號加1。和SYN一樣,一個FIN將佔用一個序號。
3) 服務器B關閉與客戶端A的連接,發送一個FIN給客戶端A。
4) 客戶端A發回ACK報文確認,並將確認序號設置爲收到序號加1。
3. 爲什麼建立連接協議是三次握手,而關閉連接卻是四次揮手呢?
建立連接時,服務端LISTEN狀態下的SOCKET當收到SYN報文的連接請求後,它可以把ACK和SYN放在一個報文裏來發送。
關閉連接時,當收到對方的FIN報文通知時,它僅僅表示對方沒有數據發送給你了;但未必你所有的數據都全部發送給對方了,所以你可能未必會馬上會關閉SOCKET,也即你可能還需要發送一些數據給對方之後,再發送FIN報文給對方來表示你同意現在可以關閉連接了,所以關閉連接的ACK報文和FIN報文多數情況下都是分開發送的。
常見的郵箱類型有哪些?
常見的郵箱類型有:免費郵箱、vip郵箱、域名郵箱、企業郵箱等等。
1. 免費郵箱
“免費郵箱”是郵件商家爲任何人免費提供的電子郵件傳輸服務,作爲交換,該網站上你請求電子郵件服務和一些個人信息的地方會顯示廣告。它更適合個人生活和娛樂的需要,卻並非那麼注重郵箱的安全和功能。
部分免費郵件SMTP服務器參考設置:
span>Email類型 | SMTP[Host]主服務器 | Port[端口號] | 是否可啓用SSL |
Gmail(Google 的網絡郵件服務) | smtp.gmail.com | 587 | True |
HotMail/Live | smtp.live.com | 25 | True |
QQ/FoxMail(Foxmail被騰訊收購) | smtp.qq.com | 25 | False |
126(網易) | smtp.126.com | 25 | False |
163(網易) | smtp.163.com | 25 | False |
Sina(新浪郵箱) | smtp.sina.com | 25 | False |
Tom | smtp.tom.com | 25 | False |
SoHu(搜狐郵箱) | smtp.sohu.com | 25 | False |
smtp.mail.yahoo.com | 25 | False |
2. vip郵箱
“vip郵箱”即郵件商家提供的收費版郵件服務,在速度、安全、穩定性、容量、附件大小限制、羣發數等方面相對好些。其SMTP服務器設置就是多了個vip字符。eg:smtp.vip.qq.com。郵箱地址:[email protected]。
3. 域名郵箱
“域名郵箱”是個性化郵件服務,能讓您用自己的域名做爲後綴即“@自己的域名”,前提是你需要一個域名(通常域名要收費)。功能比免費郵箱要多:可分配單個郵箱、規劃容量、更加的安全、更好的穩定性、個性化名稱、郵件發送量更大、附件大小限制等等。
4. 企業郵箱
“企業郵箱”是域名郵箱,但通常是指通過付費方式獲得更好服務的郵箱。eg:您公司域名爲www.abc.com,則SMTP服務器爲:mail.abc.com,郵箱地址:[email protected];
使用企業郵箱的優勢:
1) 提升公司企業形象、郵箱穩定性、郵箱反垃圾反病毒性能、郵件收發速度;
2) 通過購買服務,能適應企業不斷升級需求;
3) 爲員工分配(域名)企業郵箱,便於將流動員工所有業務聯繫保留和延續下來;
4) 監控郵件(實際爲郵件暗抄送功能),以防公司的機密和重要信息流失;
5) 獲得高性能郵件海外轉發功能,解決國際高效郵件收發、郵件營銷有效投遞等問題;
6) 出站電子郵件過濾,比如:敏感字過濾、基於政策郵件加密等等;
7) 等等。
郵件發送相關.NET類庫
在 .net1.1 ,用System.Web.Mail發送郵件。在.net2.0及之後版本,用System.Net.Mail發送郵件。主要用到了在.net2.0中新增的兩個類,分別是System.Net.Mail.MailMessage和System.Net.Mail.SmtpClient兩個類,在SMTP身份驗證方面用到了System.Net.NetworkCredential類。
1. MailMessage 類表示郵件的內容。
MailMessage常用屬性 |
||||
From |
MailAddress |
獲取或設置此電子郵件的發信人地址。 兩者區別:當 Sender 與 From 都有設定時,Mail Server 會取用 Sender 的設定發信,但郵件上的名稱會使用 From 的設定,而若不需要 Sender 和 From 同時設定時,則 Sender 可以免設,但 From 一定要設。詳細請看:《MailMessage 的 Sender 和 From? 傻傻分不清楚》 |
||
Sender |
||||
To |
MailAddressCollection |
獲取包含此電子郵件的收件人的地址集合。 |
||
CC |
MailAddressCollection |
獲取包含此電子郵件的抄送 (CC) 收件人的地址集合。 |
||
Bcc |
MailAddressCollection |
獲取包含此電子郵件的密件抄送 (BCC) 收件人的地址集合。 |
||
Attachments |
AttachmentCollection |
獲取用於存儲附加到此電子郵件的數據的附件集合。 |
||
Subject |
string |
獲取或設置此電子郵件的主題。 |
||
Body |
string |
獲取或設置郵件正文。 |
||
AlternateViews |
AlternateViewCollection |
指定一個電子郵件不同格式顯示的副本。(eg:發送HTML格式的郵件,可能希望同時提供郵件的純文本格式,以防止一些收件人使用的電子郵件閱讀程序無法顯示html內容) |
||
IsBodyHtml |
bool |
默認false。獲取或設置指示郵件正文是否爲 Html 格式的值。 |
||
Priority |
MailPriority |
默認Normal。獲取或設置此電子郵件的優先級。(Normal | Low| High) |
||
SubjectEncoding |
Encoding |
獲取或設置此電子郵件的主題內容使用的編碼。 |
||
BodyEncoding |
Encoding |
獲取或設置用於郵件正文的編碼。 |
||
ReplyToList |
MailAddressCollection |
設置接收方回覆郵件時默認的接收地址,eg:你用一個郵箱發信,但卻用另一個來收信。 (ReplyTo,表示單個回覆地址,已過期,使用ReplyToList代替) |
下面屬性想不到用在什麼場景……請高人指出使用案例,謝謝!
DeliveryNotificationOptions |
DeliveryNotificationOptions |
默認None。獲取或設置此電子郵件的發送通知。
不懂幹嘛的,設置爲OnSuccess,不會回覆我發送成功。設置爲Never,發送失敗也會回覆我。。。 |
||||
Headers |
NameValueCollection |
獲取與此電子郵件一起傳輸的電子郵件標頭。(什麼時候需要自己去設置?) |
||||
HeadersEncoding |
Encoding |
獲取或設置此電子郵件的用戶定義的自定義標題使用的編碼。 |
2. SmtpClient類用於將電子郵件發送到 SMTP 服務器以便傳遞。
SmtpClient常用屬性 |
||
Host |
string |
獲取或設置用於 SMTP 事務的主機的名稱或 IP 地址。 |
Port |
int |
獲取或設置用於 SMTP 事務的端口。 |
UseDefaultCredentials |
bool |
默認false。 若要使用默認網絡憑據,可以將UseDefaultCredentials設置爲 true,此時System.Net.CredentialCache.DefaultCredentials(應用程序系統憑證)會隨請求一起發送。 如果UseDefaultCredentials屬性設置爲 false,則連接到服務器時會將 Credentials 屬性中設置的值用作憑據。如果UseDefaultCredentials屬性設置爲 false 並且尚未設置 Credentials 屬性,則將郵件以匿名方式發送到服務器。若SMTP 服務器要求在驗證客戶端的身份則會拋出異常。 |
Credentials |
ICredentialsByHost |
獲取或設置用於驗證發件人身份的憑據。 |
ClientCertificates |
X509CertificateCollection |
指定應該使用哪些證書來建立安全套接字層 (SSL) 連接。 |
EnableSsl |
bool |
默認false。指定SmtpClient是否使用安全套接字層 (SSL) 加密連接。 |
Timeout |
int |
默認100000.獲取或設置一個值,該值指定同步重載:SmtpClient.Send()調用的超時時間。 |
自建本地SMTP服務器獲取郵件時需要使用的屬性: |
||||
DeliveryMethod |
SmtpDeliveryMethod |
默認NetworkCredential。
|
||
PickupDirectoryLocation |
string |
獲取或設置文件夾,應用程序在該文件夾中保存將由本地 SMTP 服務器處理的郵件。 |
||
下面屬性想不到用在什麼場景……請高人指出使用案例,謝謝! |
||||
TargetName |
string |
"SMTPSVC/" + this.host。獲取或設置在使用擴展保護時用於身份驗證的服務提供程序名稱 (SPN)。 |
||
ServicePoint |
ServicePoint |
獲取用於傳輸電子郵件的網絡連接。(應該會保存TCP連接,避免再次進行TCP的三次握手???) |
3. 一個簡單的郵件發送示例
- MailMessage mail = new MailMessage();
- mail.From = new MailAddress(From, FromDisplayName);
- mail.To.Add(new MailAddress(To, ToDisplayName));
- mail.Subject = "this is a test email.";
- mail.Body = "this is my test email body.<br><b>this part is in bold</b>";
- mail.IsBodyHtml = true;
- SmtpClient smtp = new SmtpClient(host, port);
- smtp.Credentials = new NetworkCredential(userName, password);
- smtp.Send(mail);
4. 郵件擴展:如何發送內嵌資源(eg:圖片、mp3等等)
詳細請看:http://www.cnblogs.com/SkyD/archive/2009/05/11/1453868.html(斯克迪亞)
通過 ContentDisposition 類實現此功能,內嵌的資源只做爲文件內容顯示,不再在附件列表中出現。ContentDisposition 類表示 MIME 協議 Content-Disposition 標頭。
對於文件附件,可以使用 ContentDisposition 的屬性來設置文件大小、文件的創建日期、上次讀取文件的日期以及上次修改文件的日期。對於所有附件,考慮到附件有可能會存儲到接收計算機上,可以設置一個建議的文件名。顯示電子郵件的軟件可以使用 ContentDisposition 中的信息,按發件人預期的方式呈現電子郵件附件。
通過 ContentDisposition 實例的Inline屬性實現郵件內嵌資源。如下:
1) 設置附件的ContentId屬性爲一個自定義名稱。
2) 設置附件的ContentDisposition.Inline屬性爲true。
3) 在郵件的HTML格式正文中以“cid:自定義名稱”的方式引用,比如ContentId設爲“face”,那麼正文中就以“cid:face”作爲其URL路徑字符串的替代即可。
代碼如下:(詳細見示例代碼)
- string picPath = Environment.CurrentDirectory + "\\附件\\PIC_Mail中文.png";
- Attachment attach_pic = new Attachment(picPath);
- // 獲取或設置此附件的 MIME 內容 ID。
- attach_pic.ContentId = "MyPic";
- // 實例郵件內容
- System.Net.Mime.ContentDisposition disposition = attach_pic.ContentDisposition;
- // 若爲內聯,則不會以附件的形式顯示,而是直接顯示爲郵件內容
- disposition.Inline = true;
- FileInfo file = new FileInfo(picPath);
- // 設置文件附件的創建日期。
- disposition.CreationDate = file.CreationTime;
- // 設置文件附件的修改日期。
- disposition.ModificationDate = file.LastWriteTime;
- // 設置文件附件的讀取日期。
- disposition.ReadDate = file.LastAccessTime;
- // 設定文件名稱 (內嵌資源設置文件名後下載下來纔有默認後綴)
- disposition.FileName = file.Name.ToString();
- mail.AddAttachment(attach_pic);
另外,可使用AlternateView類和LinkedResource類來實現內嵌資源……
1) 創建一個MailMessage對象,同時指定發送人和接收人地址。
2) 創建AlternateView來接收文本內容,創建LinkedResource來接收要嵌入的圖片或其他資源。
3) 添加LinkedResource到AlternateView
4) 添加AlternateView到MailMessage
5) 設置SmtpClient,發送email
我開發的一個發送郵件的小組件(代碼在博文開始處已給出下載地址)
爲了簡化郵件發送代碼編寫和SmtpClient實例的管理,我封裝了一個發郵件的幫助類。
這個幫助類,包含如圖幾個文件:
兩個主要類: SmtpHelper 和MailHelper
1. SmtpHelper
此類是爲了簡化構造SmtpClient實例所需的代碼量。通過SmtpHelper構造函數設置好SMTP服務器、端口號、身份憑據,再通過鏈式操作快速設置SmtpClient其他不常使用的屬性。
Eg:
- SmtpClient client = new SmtpHelper( host, port, false, userName, password)
- .SetTimeout(60*1000)
- .SmtpClient;
使用SmtpHelper類注意事項:
1) 非線程安全類.
2) 構造的SmtpClient 實例外部進行Dispose()。SmtpHelper類只簡單提供構造,不做釋放操作。
3) SmtpClient 沒有提供 Finalize() 終結器,所以GC不會進行回收,只能由外部使用完後進行顯示釋放,否則會發生內存泄露問題.
2. MailHelper
此類完成郵件的發送工作。需要結合MailInfoHelper靜態類驗證郵件信息的有效性。
1) 支持快捷添加附件、內嵌資源、地址信息、備用視圖格式;
Eg:添加內嵌資源
- // 郵件內容:"<a href=\"cid:MyPic\" target=\"_blank\">點擊在新窗口打開圖片</a>";
- string picPath = Environment.CurrentDirectory + "\\附件\\PIC_Mail中文.png";
- mail.AddInlineAttachment(picPath,"MyPic");
2) 支持在發送郵件前對郵件信息有效性進行檢查;
- Dictionary<MailInfoType, string> dic = mail.CheckSendMail();
- if (dic.Count > 0 && MailInfoHelper.ExistsError(dic))
- {
- // 反饋“錯誤+提示”信息
- msg = MailInfoHelper.GetMailInfoStr(dic))
- }
- else
- {
- if (dic.Count > 0)
- {
- // 反饋“提示”信息,但還是可以發送郵件
- msg = MailInfoHelper.GetMailInfoStr(dic);
- }
- // 發送郵件
- }
3) 支持批量同步、異步發送郵件
a) 批量同步發送郵件:實際上只是 SmtpClient.Send() 同步發送郵件的一個封裝。
b) 批量異步發送郵件
i. 待發送隊列:因爲一個SmtpClient一次只能發送一個MailMessage,不管是同步還是異步發送,所以 SmtpClient.SendAsync() 方法後必須阻塞線程直到上一封郵件發送完成,否則會拋出“正在發送郵件”的異常。所以,MailHelper爲了避免調用線程的阻塞,將待發送郵件的信息都加入到隊列中,內部啓用一個線程去執行串行化發送任務。
ii. 限流:“異步”批量發送過程中,爲了防止待發送隊列無限制的增大,導致內存溢出,我們可以通過MailHelper的GetAwaitMailCountAsync()方法監控該隊列的大小,適當的執行Thread.Sleep(time).
iii. 異步取消:可以通過MailHelper的SendAsyncCancel()方法,取消待發送隊列中的郵件繼續發送。
iv. 回調函數:異步發送完一封電子郵件後執行的回調函數。通過SendCompleted事件進行註冊。但要注意其AsyncCompletedEventArgs參數的UserState對象被改寫爲了我定義的 MailUserState 對象
MailUserState定義如下:
- /// <summary>
- /// 異步發送郵件時保存的信息,用於釋放和傳遞數據
- /// </summary>
- public class MailUserState
- {
- #region 由MailHelper內部的SendCompleted註冊的事件使用
- // 用於釋放 MailMessage 和 SmtpClient
- public MailMessage CurMailMessage { get; set; }
- public bool AutoReleaseSmtp { get; set; }
- public SmtpClient CurSmtpClient { get; set; }
- // 只發送單封郵件的時候使用此進行判斷釋放
- public bool IsSmpleMail { get; set; }
- #endregion
- /// <summary>
- /// 用戶傳遞的狀態對象
- /// </summary>
- public object UserState { get; set; }
- /// <summary>
- /// 當異步發送報錯時可通過此標識是否已經處理該異常
- /// </summary>
- public bool IsErrorHandle { get; set; }
- }
批量異步發送示例(注意回調函數的用戶信息):
- // 設置SmtpClient的回調函數
- client.SendCompleted +=(send,args) =>
- {
- AsyncCompletedEventArgs arg = args;
- MailUserState userState = arg.UserState as MailUserState;
- }
- // 在MailHelper的構造函數中決定是同步發送還是異步發送郵件
- MailHelper mail = new MailHelper(client,true,isAsync);
- for (long i = 1; i <= mailCount; i++)
- {
- if(mail.GetAwaitMailCountAsync()>1000)
- {
- // 當待發送隊列大於1000時,線程休眠1秒
- Thread.Sleep(1000);
- }
- // 設置 MailHelper 發送信息
- // ……
- // 設置每封電子郵件發送完執行回調函數的UserState
- mail.AsyncUserState = “你傳遞的對象信息”;
- // 執行批量發送郵件
- mail.SendBatchMail();
- }
- mail.SetBatchMailCount(count);
4) 批量發送郵件中,每調用一次發送方法,要使用MailHelper的Reset()對郵件內容進行重置。
注意:
a) 不重置SmtpClient。SmtpClient根據 m_autoDisposeSmtp 參數自動釋放或由外部主動釋放
b) 不重置:異步待發送隊列及隊列計數、AutoResetEvent實例、執行異步發送線程變量、是否啓用異步發送標識變量
5) 支持自動釋放SmtpClient實例
在平常郵件開發中,當在異步批量發送郵件時,我們沒辦法掌握何時釋放我們重用的SmtpClient實例。
但,我們使用MailHelper類,可以不用關心SmtpClient的釋放問題。我們通過構造函數中指定自動釋放SmtpClient的參數爲true,並且統計好批量郵件發送量之後調用 SetBatchMailCount(long preCount) 方法,MailHelper就會在(批量)同步、(批量)異步郵件全部發送完之後自動釋放SmtpClient實例。
a) 爲什麼要“重用”同一個SmtpClient實例
因爲,每次發送一封電子郵件,都必須經過TCP的三次握手與服務器建立連接,這個連接信息就保存在SmtpClient實例中,所以當進行大批量的電子郵件發送時(前提是發件地址是相同的,當然大部分場景下發件地址都是相同的),有必要重用SmtpClient實例,避免TCP不斷地發生“三次握手和四次揮手”。
b) 爲什麼要“顯示釋放”SmtpClient實例
SmtpClient類沒有 Finalize (終結器)方法,因此應用程序"必須"調用 Dispose 來顯式釋放資源。 Dispose 方法在所有建立到 Host 屬性中指定的 SMTP 服務器的連接中循環,併發送 QUIT 消息,其後平穩斷開 TCP 連接。
MailHelper組件的一個示例以及幾種方式發郵件的優劣測試
示例包含四次實驗方案和兩組複選框,如圖:
示例代碼下載後注意,請先修改如Config.cs文件的幾處紅色標識信息(如下圖),你才能正常發送郵件。
用QQ郵箱發件的注意啦,要在“設置”-“賬戶”中將“POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服務”服務都開啓才能正常發送郵件。如圖:
實驗一:單條郵件同步和異步發送(可通過添加大附件來觀察同步異步效果)
觀察:MailHelper類是如何使用的。通過大附件觀察下同步發送郵件和異步發送郵件的效果,查看下單封郵件發送
實驗二:批量郵件同步和異步發送(單個線程,單個SmtpClient實例,SendAsync())
觀察:MailHelper類中批量異步發送使用隊列方式實現的高響應性,以及批量操作如何自動釋放SmtpClient實例。觀察下
在數量較大的批量郵件發送場景中,我們可以使用多個SmtpClient(不清楚並行類庫的,請看 《異步編程:.NET4.X 數據並行》 )實例來並行發送,以提高整體發件效率。即實驗三 + 實驗四
實驗三:批量郵件同步和異步發送(平行類庫Parallel(自動分區),每個分區一個MailHelper、SmtpClient實例)
觀察:Parallel.For的自動分區 + 每個分區一個MailHelper 和SmtpClient實例來提高整體效率。但是,有個問題就是自動分區又.NET內部根據資源負載均衡自動分區,分區的效果非常不好,總會開啓過多的分區導致MailHelper和SmtpClient實例偏多,並且效率不高。現在通過
實驗四:批量郵件同步和異步發送(平行類庫Parallel(手動分區),每個分區一個MailHelper、SmtpClient實例)
觀察:Parallel.Foreach的手動分區 + 每個分區一個MailHelper 和SmtpClient實例來提高整體效率。我們自己根據業務場景和Environment.ProcessorCount內核數來決定分區數,這樣可以根據需要創建MailHelper和SmtpClient實例,並且效率非常高。在通過
另外:重用SmtpClient複選款的測試結果:如果只是簡單的純文本郵件發送(即,沒有耗時的附件內容),重用SmtpClient可提升50%的效率。(注意:需要使用批量同步方式發送進行測試。因爲異步方式會使用多個SmtpClient進行並行發送所以測試不出效率提升)
來個整個示例截圖:
本郵件發送功能分享到此結束,如果你看後覺得對你有幫助的,還請多幫推薦……推薦……如果內容有誤的,還請幫忙指出,謝謝!
原文鏈接:http://www.cnblogs.com/heyuquan/p/net-batch-mail-send-async.html