郵件之MailKit使用


電子郵件介紹

電子郵件使用的三大協議

1.電子郵件的工作原理

Internet電子郵件系統是基於客戶機/服務器方式,客戶端也叫用戶代理(User Agent),提供用戶界面,負載郵件發送的准備工作,如郵件的起草、編輯以及向服務器發送郵件或從服務器取郵件等。服務器端也叫傳輸代理(Message Transfer Agent),負責郵件的傳輸,它采用端到端的傳輸的傳輸方式,源端主機參與郵件傳輸的全過程。

2.電子郵件協議

電子郵件在發送和接收的過程中還要遵循一些基本協議和標准,這些協議主要有SMTP、POP3、IMAP、MIME等。

(1)SMTP協議

SMTP(Simple Mail Transfer Protocol,簡單郵件傳輸協議)是Internet上基於TCP/IP的應用層協議,使用於主機與主機之間的電子郵件交換。SMTP的特點是簡單,它只定義了郵件發送方和接收方之間的連接傳輸,將電子郵件有一台計算機傳送到另一台計算機,而不規定其他任何操作,如用戶界面的交互、郵件的接收、郵件存儲等。Internet上幾乎所有主機都運行着遵循SMTP的電子郵件軟件,因此使用非常普通。另一方面,SMTP由於簡單,因而有其一定的局限性,它只能傳送ASCII文本文件,而對於一些二進制數據文件需要進行編碼后才能傳送。

(2)POP3協議和IMAP協議

電子郵件用戶要從郵件服務器讀取或下載郵件時必須要有郵件讀取協議。現在常用的郵件讀取協議有兩個,一個是郵局協議的第三版本(POP3,Post Office Protocol Version 3),另一個是因特網報文存取協議(IMAP,Internet Message Access Protocol)。

POP3是一個非常簡單、但功能有限的郵件讀取協議,大多數ISP都支持POP3。當郵件用戶將郵件接收軟件設定為POP3閱讀電子郵件時,每當使用者要閱讀電子郵件時,它都會把所有信件內容下載至使用者的計算機,此外,他可選擇把郵件保留在郵件服務器上或是不保留郵件在服務器上。無IMAP是另一種郵件讀取協議。當郵件用戶將郵件接收設定IMAP閱讀電子郵件時,它並不會把所有郵件內容下載至計算機,而只下載郵件的主題等信息。

(3)多途徑Internet郵件擴展協議

多用途Internet郵件擴展協議(MIME,Multipose Internet Mail Extensions)是一種編碼標准,它解決了SMTP只能傳送ASCII文本的限制。MIME定義了各種類型數據,如聲音、圖像、表格、二進制數據等的編碼格式,通過對這些類型的數據編碼並將它們作為郵件中的附件進行處理,以保證這些部分內容完整、正確地傳輸。因此,MIME增強了SMTP的傳輸功能,統一了編碼規范。

根據smtp協議規定,其實從本質上來說郵件就不是安全的。我們可以做到客戶端到郵件服務器使用ssl加密。但是郵件服務器上的郵件使用的是明文存儲。無論你多么小心使用郵件也抵擋不住郵件服務器被攻破吧。所以最有安全的方法其實就是將郵件加密。把密文放到郵件服務器上,那么密碼泄露,郵件泄露就不會怕了。

參考:電子郵件使用的三大協議 

POP3、SMTP、IMAP 和 Exchange 的區別

SMTP, POP3, IMAP 都是mail server上的service。

簡單地說,SMTP管‘發’,POP3/IMAP管‘收’。

舉個例子,你坐在電腦邊用mail client寫完郵件,點擊‘發送’。這時你的mail client會發消息給郵件服務器上的SMTP service。這時有兩種情況:如果郵件的收信人也是處於同一個domain,比如從http://163.com發送給163的郵箱,SMTP service只需要轉給local的POP3 Service即可。如果郵件收信人是另外的domain,比如http://163.com發送給http://sina.com, SMTP service需要通過詢問DNS, 找到屬於sina的SMTP service的hostSMTP service收到郵件后轉給負責接收郵件的POP3 service。

POP3 service和IMAP的區別主要是:POP3是比較老的protocol,主要為了解決本地機器和遠程郵件服務器鏈接的問題,每次郵件會download到本地機器,然后從遠程郵件服務器上刪掉(當然特殊config除外),然后進行本地編輯。這樣的問題是如果從多個終端鏈接服務器,只有第一個下載的能看到。POP3的好處是應用廣泛,壞處是無法同步消息;一旦下載服務端即消失(你可以設置在服務端保存副本,但這並不改變協議的本質);無法同步聯系人、日歷和子郵件目錄。現在pop4正在討論中。IMAP是比較新的protocol,可以將郵件分文件夾整理,然后這些信息也存在遠程的郵件服務器上,讀取郵件后,服務器上不刪除。原理上IMAP應該是相當於oneline編輯,但現在的mail client基本都有在本地存copy的功能。

Exchange Server是微軟公司的一套電子郵件服務組件。除傳統的電子郵件的存取、儲存、轉發作用外,在新版本的產品中亦加入了一系列輔助功能,如語音郵件、郵件過濾篩選、OWA(基於Web的電子郵件存取)。Exchange Server支持多種電子郵件網絡協議,如SMTP、POP3、IMAP4。

Exchange Server是個消息與協作系統,Exchange server可以被用來構架應用於企業、學校的郵件系統甚至於免費郵件系統。也可以用於開發工作流,知識管理系統,Web系統或者是其他消息系統。

從上我們可以看到Exchange和IMAP/SMTP/POP3並不是同一概念,前者是微軟提供一個郵箱服務器產品/雲服務,后者是電子郵件協議。Exchange協議可供用戶同步郵件、聯系人、日歷及其他所有Exchange對象。由於這個協議需要部署Exchange服務器,因此通常為公司或者機構賬號所用。它的好處是:全郵件同步;郵件保存在服務器上;支持絕大部分移動設備、聯系人、日歷和數據同步;在服務器域中郵件可撤回並修改。壞處是必須部署昂貴的Exchange服務器;郵件管理員可以控制你的終端設備權限並能看到郵件收發狀態;同樣會有同步問題。

參考:POP3, SMTP, IMAP 和 Exchange 的區別

MailKit

1、Mimekit官網

MimeKit與MailKit都是開源的.net處理郵件的庫。

包括MailKit的介紹、常見的問題、API接口等

2、MailKit的使用心得

Mailkit 支持 Pop3、IMAP,STMP,是目前.Net端最全的郵件開源項目了。

1、Pop3:跟其他的pop3操作類沒有太大區別,跟OpenPop.NET等都差不多

2、IMAP:功能上比Pop3要強大太多,優勢是功能強大、可以搜索郵箱的所有文件夾,Pop3只能搜索INBOX(收件箱),如果要做收信,還是IMAP首選。缺點是每一次操作都會與服務器同步,比如讀取了郵件,服務器上也會變為已讀,而Pop3不會。

如果做郵箱收信的話,還是必須同時支持 Pop3、IMAP。

IMAP

1、A02 NO SELECT Unsafe Login. Please contact kefu@1888.com for help

imapClient.Identify 的使用

var clientImplementation = new ImapImplementation
{
Name = "sssssd",
Version = "2.0"
};
var serverImplementation = imapClient.Identify(clientImplementation);

這句代碼在登錄完后需要執行,不然無法拉取文件夾。

這個命令主要是表面客戶端身份的,參數name和version的值,可以按照需要去寫,如上面的163郵箱就沒有要求,可以隨便填,但有些郵件服務器是有要求的,只有服務器認可的客戶端和版本才可以正常使用,服務器會拒絕非法客戶端的,一切看郵件服務端的要求。

參考:mailkit----163郵箱登錄拉取郵件的坑

2、Search

IList<UniqueId> Search(SearchQuery query, CancellationToken cancellationToken = default);

在文件夾中搜索與指定查詢匹配的郵件

3、Fetch

Fetch(IList<UniqueId>, MessageSummaryItems, IEnumerable<String>, CancellationToken)

獲取指定消息UID的消息摘要。

MessageSummaryItems:要獲取的消息摘要項

IEnumerable<String>:所需的標題字段。

4、MoveTo

Nullable<UniqueId> IMailFolder.MoveTo(UniqueId, IMailFolder, CancellationToken)

從源文件夾,將指定的消息移到目標文件夾。

UniqueId:要移動的郵件的uid

返回:郵件在目標文件夾中的UID(如果有); 否則為null。

注意:在本地文件夾(源)得有這封郵件的UId, 返回才會有新的Uid

5、已讀回執

都是用此參數  HeaderId.DispositionNotificationTo

IList<IMessageSummary> messages = folder.Fetch(uids, MessageSummaryItems.UniqueId | MessageSummaryItems.Full, fields);

        private HashSet<string> CreateHeaderFields()
        {
            //先抓取郵件頭列表
            HashSet<string> fields = new HashSet<string>();
            fields.Add(HeaderId.Subject.ToHeaderName());//標題
            fields.Add(HeaderId.MessageId.ToHeaderName());//MessageId
            fields.Add(HeaderId.DispositionNotificationTo.ToHeaderName());//請求已讀回執
            return fields;
        }

注意:【阿里雲郵箱服務器】拉取郵件頭時 可能拉取不到 DispositionNotificationTo,但是 拉取郵件體的時候 是可以拉取到的,若是在獲取郵件頭時判斷是否有已讀回執,此時需要在獲取郵件體時做更新。

6、the ImapClient is currently busy processing a command in another thread. Lock the SyncRoot property to properly synchronize your threads   

有可能文件夾未關閉 導致   folder.Close() ;

參考:https://stackoverflow.com/questions/29222413/thread-safe-getfolder-using-mailkit

看下流程上,有哪里多的調用,在一個完整的流程中 盡量不要重復連接imap和重復打開關閉文件夾。

7、The IMAP server replied to the 'DELETE' command with a 'NO' response.

現象:在郵件客戶端上刪除文件夾時失敗,報此問題。

此種情況是IMAP服務器出於某種原因 不讓你(客戶端)刪除此文件夾。

可以用其他郵件客戶端來試下,能不能刪除掉【eg:foxmai】

8、一個封鎖操作被對 WSACancelBlockingCall 的調用中斷。

Unable to read data from the transport connection: 一個封鎖操作被對 WSACancelBlockingCall 的調用中斷。.

 ---> System.Net.Sockets.SocketException (10004): 一個封鎖操作被對 WSACancelBlockingCall 的調用中斷。

無法從傳輸連接讀取數據

可能原因:調用Close后,線程恰好繼續向網絡緩沖區中讀取數據

首先找到報錯代碼的位置,再進行修改

參考:一個封鎖操作被對 WSACancelBlockingCall 的調用中斷 ErrorCode=10004

9、imap連接時:The handshake failed due to an unexpected packet format

由於意外的數據包格式,握手失敗

解決方案: 暫無, 記程序日志看看。

IMAP接口:

http://www.mimekit.net/docs/html/M_MailKit_Net_Imap_ImapClient_ConnectAsync_1.htm

client.Connect(strIP, mailServer.ServerPort, SecureSocketOptions.Auto, mailConnect.CancellationToken);

參數options:用SecureSocketOptions.Auto,不要用SecureSocketOptions.SslOnConnect

異常解釋:

http://www.mimekit.net/docs/html/T_MailKit_Security_SslHandshakeException.htm

SSL/TLS握手期間出錯時引發的異常。

發生此異常時,通常意味着您連接的IMAP、POP3或SMTP服務器使用的SSL證書已過期或不受系統信任

通常情況下,郵件服務器將使用自簽名證書,而不是使用已由可信證書頒發機構簽名的證書。如果系統無法驗證郵件服務器的證書,因為該證書未由已知和受信任的證書頒發機構簽名,則會發生此異常。

您可以通過提供自定義RemoteCertificateValidationCallback並在客戶端的ServerCertificateValidationCallback屬性上設置它來解決此問題。

很可能,您希望將服務器證書的指紋與已知值進行比較,並/或提示用戶接受證書(類似於您可能看到的web瀏覽器在遇到不受信任的證書時所做的操作)。

更多參考:

https://www.limilabs.com/blog/the-handshake-failed-due-to-an-unexpected-packet-format

由於意外的數據包格式,握手失敗

很可能您的服務器需要顯式SSL,有時也稱為TLS。

它被稱為顯式SSL模式,因為在建立連接之后,客戶機顯式地向服務器發出一個命令,以啟動SSL/TLS協商。

這與隱式SSL模式不同,在隱式SSL模式中,SSL協商是在成功連接之后啟動的。在隱式模式下,服務器和客戶機知道使用SSL,因為客戶機使用默認協議端口,這通常用於安全通信。

首先嘗試不使用SSL連接到服務器:

client.Connect("mail.example.com");

然后,在登錄之前,啟動顯式SSL協商。不同協議的命令名不同:

 

顯式SSL(又名TLS

無論使用哪種協議(IMAP、POP3或SMTP),代碼都完全相同。

client.Connect("mail.example.com");
client.StartTLS();

StartTLS方法與服務器協商安全協議,並使用SSL或TLS保護通道。現在,你的連接安全了。

SSL vs TLS vs STARTTLS

請注意,您的服務器可能根本不需要SSL/TLS。在這種情況下,只需使用Connect方法。

 

啟用的SSL協議

在極少數情況下,“握手失敗…”錯誤可能表示TLS在客戶機或服務器上配置不正確。

在顯式模式下,可以強制使用SSL v3.0而不是TLS:

client.SSLConfiguration.EnabledSslProtocols = SslProtocols.Ssl3;
client.Connect("mail.example.com");
client.StartTLS();

也可以強制使用SSL v3.0,而不是隱式模式下的TLS:

client.SSLConfiguration.EnabledSslProtocols = SslProtocols.Ssl3;
client.ConnectSSL("mail.example.com");

 

自簽名證書

請記住,可以使用ServerCertificateValidate事件忽略SSL證書錯誤:

static void Validate(object sender, ServerCertificateValidateEventArgs e)
        {
            const SslPolicyErrors ignoredErrors =
                SslPolicyErrors.RemoteCertificateChainErrors |
                SslPolicyErrors.RemoteCertificateNameMismatch;

            if ((e.SslPolicyErrors & ~ignoredErrors) == SslPolicyErrors.None)
            {
                e.IsValid = true;
                return;
            }
            e.IsValid = false;
        }

        client.ServerCertificateValidate += Validate;
        client.Connect...

10、用客戶端發郵件時,已發送 文件夾中有兩封一樣的郵件

因為 客戶端發郵件時,會將郵件保存在已發送,然后同步到服務器。

sentFolder.Open(FolderAccess.ReadWrite); 
UniqueId? uid = sentFolder.Append(message, MessageFlags.Seen);//添加為已讀
        if (uid == null)
          {
            uid = sentFolder.UidNext;
            mailInfo.Uid = (uid.Value.Id - 1).ToString();
          }
         else
          {
            mailInfo.Uid = uid.Value.Id.ToString();
          }

163郵箱:網頁上有設置,如下,服務器會有一份保存在 已發送。

 

因為 客戶端發郵件時,會將郵件保存在已發送,然后同步到服務器,此時163服務器uid返回不為null,假設為100,自己保存的uid=99【網頁本身設置的保存】。

所以客戶端上點擊 已發送時,會將網頁上的同步下來。所以就有兩封了。

阿里雲郵箱:也有設置

但是阿里雲郵箱內部有處理, uid 會返回null。通過uid = sentFolder.UidNext;

取到同步上去的郵件的uid,假設為99,自己保存的其實也是99,即同一份。所以本地和遠程只會有一份。

11、Mailkit 突然就卡住

在使用過程中出現跑着跑着Mailkit 突然就卡住了,既不報錯,也沒有異常,就在那卡着,無論是Connect、Login又或者是其他任何操作,即便你設置了imapClient.Timeout、pop3Client.Timeout。

查看了源碼之后發現,雖然調用的是同步方法(Mailkit 支持異步),可是Mailkit 內部實際還是用的等待異步來執行的,而異步執行中有一個很重要的參數cancellationToken沒有填,因為這個參數支持默認值,一開始我以為既然有默認值,而且同步執行應該可以不需要設置這個參數,設置 Timeout就可以了,事實證明還是太年輕了。cancellationToken、Timeout分管的是兩個不同的東西。

解決方法:在每個帶有cancellationToken參數的方法中都帶上自定義參數

var TokenSource = new CancellationTokenSource(30000);//30s超時
folder.Open(FolderAccess.ReadWrite, cancellationToken: TokenSource.Token);
var msg = folder.GetMessage(Index, cancellationToken: TokenSource.Token); 
folder.Close(cancellationToken: TokenSource.Token);
imapClient.Identify(clientImplementation, cancellationToken: TokenSource.Token);

IMAP、Pop3都需要!!!設置好之后再也沒有出現卡住不動的情況

POP3

1、Pop3Client.SupportsUids

獲取Pop3Client是否支持通過UID引用消息

說明:並非所有服務器都支持通過UID引用消息,因此應在使用GetMessageUid(Int32,CancellationToken)和GetMessageUids(CancellationToken)之前檢查此屬性。

如果服務器不支持UID,則所有采用UID參數以及GetMessageUid(Int32,CancellationToken)和GetMessageUids(CancellationToken)的方法都將失敗。

2、Pop3Client.Count

獲取POP3服務器上可用消息的數量。

一旦通過身份驗證,Count屬性將設置為POP3服務器上可用消息的數量。

3、Pop3Client.GetMessageHeaders(IList<Int32>, CancellationToken)

獲取指定索引處的郵件頭

當POP3服務器支持管道擴展時,此方法可能比為每條消息使用GetMessageHeaders(Int32,CancellationToken)更為有效,因為它將批量處理命令以減少延遲。

枚舉:Pop3Capabilities Enumeration

Pipelining:服務器支持PIPELINING擴展,從而允許客戶端一次向服務器批處理多個請求。

4、Pop3Client.GetMessageUidsAsync

異步獲取可用消息UID的完整列表

說明:並非所有服務器都支持UID,因此您應該首先檢查UIDL標志的Capabilities屬性或SupportsUids便利屬性。

SMTP

1、SendMail throw a exception:MailKit.Net.Smtp.SmtpCommandException: RCPT (xx@yyy.com) dosn't exist

意思是 收件人地址在服務器上已經不存在

RCPT是 SMTP協議里的一個命令,用於標識郵件的收件人;以 RCPT TO: 的形式使用

注意

1、163郵箱收件時間 

默認設置了 收取最近30天郵件。

2、回執

阿里雲網頁版郵箱的收信設置

  • 始終不發送回執:不管發件方是否 勾選了 要發送回執,收到郵件后都不用發回執
  • 始終發送回執:要對方勾選了已讀回執,才會發已讀回執。
  • 有需要時發送回執:收到郵件后,會提示你發送回執與否,按鈕:“確定”、“取消”

3、163郵箱采用IMAP協議收取郵件時,報錯登錄不安全

報錯內容:The IMAP server replied to the 'EXAMINE' command with a 'NO' response: EXAMINE Unsafe Login. Please contact kefu@188.com for help

(IMAP服務器使用“否”響應回復了“ EXAMINE”命令:EXAMINE不安全登錄。 請聯系kefu@188.com尋求幫助)

解決方法:

對163郵箱設置: 收郵件NO Select Unsafe Login. Please contact kefu解決辦法

4、163郵箱采用IMAP發送郵件失敗,DT:SPM 163 smtp X

網上看說是 郵件被163當做垃圾郵件了,這時一般會接到163的退信 郵件。

554 DT:SPM smtp3 退信解決辦法
出現提示:DT:SPM smtp1, ********–.******** http://mail.163.com/help/help_spam_16.htm?ip=********&hostid=smtp1&time=********
DT:SPM 是出錯信息的關鍵詞,可以在這個網頁中找到出錯的原因 http://help.163.com/09/1224/17/5RAJ4LMH00753VB8.html
原因:550 DT:SPM 郵件正文帶有很多垃圾郵件特征或發送環境缺乏規范性。需調整郵件內容或優化發送環境;
原因分析
郵件中帶有敏感關鍵詞,例如促銷,發票等。
郵件中包含超級鏈接,或者超級鏈接太多。
垃圾郵件特征比較明顯,例如:只有一張圖片,或只有一張圖片。
發送相同的郵件內容太多了。處理這種情況的方法是:換其他郵箱發送,或調整郵件內容。

再者,還可以將 發件人自己加入到 抄送中。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM