C# .NET 中的 POP3 客户端






4.80/5 (156投票s)
一个用 C# .NET 编写的 POP3 客户端,用于读取和处理电子邮件(包括附件)。
引言
很久以前,我曾被要求开发一个软件,用于从电子邮件中提取正文和主题行。“嗯……”,我想,“连接到 110 端口的邮件服务器,发送 POP3 命令,接收数据,搞定!”。确实,在我第一次尝试时,这简直是小菜一碟:读取电子邮件——没问题。我在公司工作的同事们纷纷宣扬我们的能力:“是啊,伙计,我们可以自动处理电子邮件,轻而易举”。
客户随后提出了更多问题:“我们可以将邮件以富文本或 HTML 格式发送吗?”。“是的,当然可以!!”。“关于自动处理呢?”。“嘿——你在和邮件之王说话!!”。“处理多个附件,WAV、MP3、JPEG 呢?”。“呃……我稍后答复你……”。事情并不像我想的那么容易……
起初我发现编写起来相当困难的原因主要是 MIME 的写法以及它初看起来有多么丑陋。这里有一个示例,其中包含两个多部分块(我稍后会解释)。
Received: by Mailserver
id 01C3EFF7.990BBDF0@TEST; Tue, 11 Feb 2003 17:02:00 -0000
Message-ID: 2CB86919E23ED51191840060080C3DAE02320B76@MAILSERVER
From: Desmond McCarter
To: testemail
Subject: FW: my subject
Date: Tue, 11 May 2003 17:01:59 -0000
MIME-Version: 1.0
Content-Type: multipart/mixed;
boundary="----_=_NextPart_000_01C3EFF7.990BD65A"
This message is in MIME format. Since your mail reader does not understand
this format, some or all of this message may not be legible.
------_=_NextPart_000_01C3EFF7.990BD65A
Content-Type: text/plain;
charset="iso-8859-1"
-----Original Message-----
From: Lisa Cleary [mailto:lisa@cleary.com]
Sent: 11 May 2003 16:17
To: 'Desmond McCarter'
Subject: RE: Test
------_=_NextPart_000_01C3EFF7.990BD65A
Content-Type: application/vnd.ms-excel;
name="test.xls"
Content-Transfer-Encoding: base64
0M8R4KGxGuEAAAAAAAAAAAAAAAAAAAAAPgADAP7/CQAGAAAAAAAAAAAAAAAEAAAAwQEAAAAAAAAA
EAAA/v///wAAAAD+////AAAAAL0BAAC+AQAAvwEAAMABAAD/////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////8J
CBAAAAYFAP4czQfJQAAABgEAAOEAAgCwBMEAAgAAAOIAAABcAHAADQAAV0ggU21pdGggTmV3cyAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEIAAgCwBGEBAgAAAMABAAA9AQQA
AQD8AJwAAgAOABkAAgAAABIAAgAAABMAAgAAAK8BAgAAALwBAgAAAD0AEgAXB6b/WC+gIzgAAQAA
AAEAWAJAAAIAAACNAAIAAAAiAAIAAAAOAAIAAQC3AQIAAADaAAIAAAAxABoAyAAAAP9/kAEAAAAA
.
MIME(多用途互联网邮件扩展):一个快速粗略的指南
通过互联网传输的数据是以字节集合(即 8 位集合)的形式发送的。这些信息包括文本文件、CSV 文件,甚至是 JPEG 或电影。“嘿”,你可能会说,“你不能以字节集合的形式发送二进制数据!”。是的,你可以,只要使用合适的编码方案:例如,使用 base 64 算法(在你的 .NET 框架中查看 System.Convert.ToBase64String
方法)。这些信息(我们在邮件语境中讨论)还包括主题、正文和转发的项目。对于客户端(发送邮件)和服务器(读取邮件)来说,它们应该相互理解,并且为了做到这一点,它们必须遵循(发送和接收数据)MIME 格式。
在上面的 MIME 示例片段中,你可以轻松理解基本字段。
“发件人:” - 谁发送了电子邮件,“收件人:” - 谁收到了电子邮件,“主题:” - 电子邮件的主题,以及“日期:” - 电子邮件发送的日期/时间。
“内容类型:” 决定了电子邮件包含的内容类型。对于简单的文本电子邮件(即没有附件),这通常是“text/plain”。然而,你也可以看到(我希望你确实能看到)这封邮件实际上包含一个附件:test.xls。包含附件的电子邮件的 MIME 内容类型是“multipart/mixed”。这意味着电子邮件包含被分成多个部分的数据:正文和附件(或在此情况下为“附件”)等。边界(boundary="----_=_NextPart_000_01C3EFF7.990BD65A")标识了这些部分的开始和结束。在我这个例子(以及大多数电子邮件)中,正文是这个多部分电子邮件的第一部分。正文的开始由第一个边界声明标识。
------_=_NextPart_000_01C3EFF7.990BD65A
Content-Type: text/plain;
charset="iso-8859-1"
-----Original Message-----
From: Lisa Cleary [mailto:lisa@cleary.com]
Sent: 11 May 2003 16:17
To: 'Desmond McCarter'
Subject: RE: Test
从上面的 MIME 文本中,你还可以看到这一部分也包含了它的内容类型,即正文的格式:text/plain。多部分电子邮件的所有部分都有其头部定义,然后是一个空行,然后是实际的正文。
这个多部分电子邮件的下一部分是附件。
------_=_NextPart_000_01C3EFF7.990BD65A
Content-Type: application/vnd.ms-excel;
name="test.xls"
Content-Transfer-Encoding: base64
0M8R4KGxGuEAAAAAAAAAAAAAAAAAAAAAPgADAP7/CQAGAAAAAAAAAAAAAAAEAAAAwQEAAAAAAAAA
EAAA/v///wAAAAD+////AAAAAL0BAAC+AQAAvwEAAMABAAD/////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////8J
CBAAAAYFAP4czQfJQAAABgEAAOEAAgCwBMEAAgAAAOIAAABcAHAADQAAV0ggU21pdGggTmV3cyAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEIAAgCwBGEBAgAAAMABAAA9AQQA
AQD8AJwAAgAOABkAAgAAABIAAgAAABMAAgAAAK8BAgAAALwBAgAAAD0AEgAXB6b/WC+gIzgAAQAA
AAEAWAJAAAIAAACNAAIAAAAiAAIAAAAOAAIAAQC3AQIAAADaAAIAAAAxABoAyAAAAP9/kAEAAAAA
.
再次注意,第二个也是最后一个“多部分部分”(即附件)以边界声明开始。还要注意,内容类型已经定义,并且附件的名称和编码方案也已定义,这使得它能够以字节格式通过互联网发送。你需要注意,如果这封邮件有另一个附件,那么第二个附件(第三个“多部分部分”)将以相同的边界声明开始,依此类推。
MIME 实际上是面向对象的
我在构建 POP3 库时犯的第一个错误是使用不适合的语言:C。编写和开发花费了太长时间,而且确实变得非常混乱。开发和测试我的库花费了大约 3 周时间,而在 C# 中,这只需要一天半!!原因在于,可以说,MIME 是一种面向对象的格式:多部分电子邮件的每个部分(即使是简单的 text/plain 邮件的正文 + 主要头部等)都可以被视为对象。这是我用 C# 编写它的主要原因之一(也可以使用 Java 甚至 J2EE,但是……)。
代码
我编写的代码以一个名为 Pop3Client
的类开始。这个类用于实例化与 POP3 服务器的连接。
Pop3Client email = new Pop3Client("user", "password", "mail.server.com");
然后,你可以这样打开收件箱:
email.OpenInbox();
要转到第一封电子邮件,然后调用 NextEmail()
方法,如果存在“下一封电子邮件”,它将返回 true
,如果不存在这样的电子邮件,则返回 false
。还有一个 IsMultipart
单例,你可以使用它来检查电子邮件是否有多部分(即附件)。以下是代码可能样子的示例:
try {
Pop3Client email = new Pop3Client("user", "password", "mail.server.com");
email.OpenInbox();
while( email.NextEmail())
{
if(email.IsMultipart)
{
IEnumerator enumerator = email.MultipartEnumerator;
while(enumerator.MoveNext())
{
Pop3Component multipart = (Pop3Component)
enumerator.Current;
if( multipart.IsBody )
{
Console.WriteLine("Multipart body:"+
multipart.Body);
}
else
{
Console.WriteLine("Attachment name="+
multipart.Name); // ... etc
}
}
}
}
email.CloseConnection();
}
catch(Pop3LoginException)
{
Console.WriteLine("You seem to have a problem logging in!");
}
我还在这个类库中实现了其他功能,包括保存附件(目前是自动完成的)的原始格式、获取文件名、扩展名等的 getter。
看看并告诉我你的想法:我确实觉得编写和探索它很有趣!!