Skip to main content
 首页 » 编程设计

c#之如何比 OpenPop.dll 更快地解析电子邮件

2024年01月26日32三少

可以使用 OpenPop.dll。

    Pop3Client objPOP3Client = new Pop3Client(); 
    int intTotalEmail = 0; 
    DataTable dtEmail = new DataTable(); 
    object[] objMessageParts; 
 
    try 
    { 
        dtEmail = GetAllEmailStructure(); 
 
        if (objPOP3Client.Connected) 
            objPOP3Client.Disconnect(); 
 
        objPOP3Client.Connect(strHostName, intPort, bulUseSSL); 
        try 
        { 
            objPOP3Client.Authenticate(strUserName, new Common()._Decode(strPassword)); 
            intTotalEmail = objPOP3Client.GetMessageCount(); 
 
            AddMapping(); 
 
            for (int i = 1; i <= intTotalEmail; i++) 
            { 
                objMessageParts = GetMessageContent(i, ref objPOP3Client, dtExistMailList); 
 
                if (objMessageParts != null && objMessageParts[0].ToString() == "0") 
                { 
                    AddToDtEmail(objMessageParts, i, dtEmail, dtUserList, dtTicketIDList, dtBlacklistEmails, dtBlacklistSubject, dtBlacklistDomains); 
                } 
            } 
        } 
        catch (Exception ex) 
        { 
        } 
    } 
    catch (Exception ex) 
    { 
        ParserLogError(ex, "GetAllEmail()"); 
    } 
    finally 
    { 
        if (objPOP3Client.Connected) 
            objPOP3Client.Disconnect(); 
    } 
 
    // function 
 
     public object[] GetMessageContent(int intMessageNumber, ref Pop3Client objPOP3Client, DataTable dtExistingMails) 
  { 
    object[] strArrMessage = new object[10]; 
    Message objMessage; 
    MessagePart plainTextPart = null, HTMLTextPart = null; 
    string strMessageId = ""; 
 
    try 
    { 
        strArrMessage[0] = ""; 
        strArrMessage[1] = ""; 
        strArrMessage[2] = ""; 
        strArrMessage[3] = ""; 
        strArrMessage[4] = ""; 
        strArrMessage[5] = ""; 
        strArrMessage[6] = ""; 
        strArrMessage[7] = null; 
        strArrMessage[8] = null; 
        strArrMessage[7] = ""; 
        strArrMessage[8] = ""; 
 
        objMessage = objPOP3Client.GetMessage(intMessageNumber); 
        strMessageId = (objMessage.Headers.MessageId == null ? "" : objMessage.Headers.MessageId.Trim()); 
 
        if (!IsExistMessageID(dtExistingMails, strMessageId)) //check in data base message id is exists or not  
        { 
            strArrMessage[0] = "0"; 
            strArrMessage[1] = objMessage.Headers.From.Address.Trim();     // From EMail Address 
            strArrMessage[2] = objMessage.Headers.From.DisplayName.Trim(); // From EMail Name 
            strArrMessage[3] = objMessage.Headers.Subject.Trim();// Mail Subject      
            plainTextPart = objMessage.FindFirstPlainTextVersion(); 
            strArrMessage[4] = (plainTextPart == null ? "" : plainTextPart.GetBodyAsText().Trim()); 
            HTMLTextPart = objMessage.FindFirstHtmlVersion(); 
            strArrMessage[5] = (HTMLTextPart == null ? "" : HTMLTextPart.GetBodyAsText().Trim()); 
            strArrMessage[6] = strMessageId; 
            List<MessagePart> attachment = objMessage.FindAllAttachments(); 
            strArrMessage[7] = null; 
            strArrMessage[8] = null; 
            if (attachment.Count > 0) 
            { 
                if (attachment[0] != null && attachment[0].IsAttachment) 
                { 
                    strArrMessage[7] = attachment[0].FileName.Trim(); 
                    strArrMessage[8] = attachment[0]; 
                } 
            } 
        } 
        else 
        { 
            strArrMessage[0] = "1"; 
        } 
    } 
    catch (Exception ex) 
    { 
        ParserLogError(ex, "GetMessageContent()"); 
    } 
    return strArrMessage; 
 } 

但是,我想让它比上面的 OpenPop.dll 更快。所以请让我知道是否有任何其他技术可以解析邮件。

请检查代码然后告诉我。

提前致谢

请您参考如下方法:

but, i want to make it faster than above OpenPop.dll. so please let me know if any other technique are there for parsing mails.

在您的 GetMessageContent() 方法中,第 1 个消耗大量时间的地方是:

objMessage = objPOP3Client.GetMessage(intMessageNumber); 

下载消息的网络 I/O 部分无法真正优化,但 OpenPop.NET 的解析器很慢(基于我自己的性能测试)。

MimeKit在解析电子邮件时比 OpenPop.NET 快 25 倍。

OpenPop.NET 的 MIME 解析器的主要性能问题之一是它使用 StreamReader 进行解析(由于不必要的字符集转换、一次读取 1 行等,速度很慢 - 我有另一个分析在此处使用 StreamReader 进行解析的电子邮件库:https://stackoverflow.com/a/18787176/87117)。

还有一个问题是,OpenPop.NET 的解析器在解析/解码之前还使用 Regex 从 header 字符串中删除 CFWS(注释和折叠空格)。这很贵。最好编写一个可以处理 CFWS 的良好分词器。

如果您对我用来将 MimeKit 优化得如此之快(与高度优化的 C 实现一样快或更快)的其他一些技术感兴趣,我写了一些关于此的博客文章:

Optimization Tricks used by MimeKit: Part 1

我在第 1 部分中谈到的优化总结是替换像这样扫描行尾的循环:

while (*inptr != (byte) '\n') 
    inptr++; 

使用更快的循环,像这样:

int* dword = (int*) inptr; 
 
do { 
    mask = *dword++ ^ 0x0A0A0A0A; 
    mask = ((mask - 0x01010101) & (~mask & 0x80808080)); 
} while (mask == 0); 
 
inptr = (byte*) (dword - 1); 
while (*inptr != (byte) '\n') 
    inptr++; 

这将性能提高了 20%(尽管在非 x86 架构上,它要求“双字”是 4 字节对齐的)。

Optimization Tricks used by MimeKit: Part 2

在第 2 部分中,我将讨论编写更优化的 System.IO.MemoryStream 版本。 MemoryStream 的问题在于它必须将 1 个连续的内存块与内容保持在一起,这意味着当您向它写入更多数据并且它必须调整其内部字节数组的大小时,它必须将内容复制到新数组(这是昂贵的,尤其是当流中的数据量很大时)。

为了解决这个性能瓶颈,我编写了一个 MemoryBlockStream,它不需要使用连续的内存块 - 它使用字节数组的链表。当您溢出当前缓冲区时不必调整字节数组的大小,它只需分配另一个 2048 字节的数组,数据将溢出到该数组并将其附加到链表。

注意:MimeKit 本身只做电子邮件解析,它不做 POP3 或 SMTP 或 IMAP。如果你想要那种功能,我还编写了一个基于 MimeKit 的库,它也可以做到这一点:MailKit

更新:

使用 MailKit(根据要求)下载/解析所有邮件的示例代码:

using System; 
using System.Net; 
 
using MailKit.Net.Pop3; 
using MailKit; 
using MimeKit; 
 
namespace TestClient { 
    class Program 
    { 
        public static void Main (string[] args) 
        { 
            using (var client = new Pop3Client ()) { 
                client.Connect ("pop.gmail.com", 995, true); 
 
                // Note: since we don't have an OAuth2 token, disable 
                // the XOAUTH2 authentication mechanism. 
                client.AuthenticationMechanisms.Remove ("XOAUTH2"); 
 
                client.Authenticate ("joey@gmail.com", "password"); 
 
                int count = client.GetMessageCount (); 
                for (int i = 0; i < count; i++) { 
                    var message = client.GetMessage (i); 
                    Console.WriteLine ("Subject: {0}", message.Subject); 
                } 
 
                client.Disconnect (true); 
            } 
        } 
    } 
}