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