如何使用 IMAP 协议访问电子邮件






4.53/5 (24投票s)
本文将介绍如何使用 IMAP 协议访问电子邮件。它将解释如何使用一些常见的 IMAP 命令。
引言
IMAP (Internet Message Access Protocol) 是一种电子邮件消息协议,允许从服务器传输电子邮件。该协议基于从客户端(如 Mozilla Thunderbird 或 Microsoft Outlook)发送的命令。这些命令允许您登录邮箱、获取消息总数和未读消息数、查看消息以及创建、删除和重命名邮箱。
有许多适用于各种编程语言的 IMAP 控件,也有许多解释如何使用这些控件的文章,但如果您想了解该协议是如何被用来帮助您开发自己的 IMAP 控件呢?您可能想开发一个运行在桌面上的小应用程序,通知您新邮件,或者仅仅显示您有多少未读邮件。如果您是一位非常有冒险精神的开发者,您可以尝试开发自己的电子邮件客户端。
在本文中,我将介绍如何连接到邮箱以检索电子邮件。本文旨在简要介绍一些主要的 IMAP 命令。为了使代码更易于理解,我将不讨论任何错误处理。您可以自己添加错误处理。
连接到邮箱
IMAP 在端口 143 上运行。要连接到邮箱,我们需要使用主机名连接到此端口号。
下面的列表 1.1 显示了我们需要包含在项目中的所有命名空间。
列表 1.1
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Text.RegularExpressions;
现在我们已经包含了所需的命名空间,让我们开始编码,声明所有需要的类。下面的列表 1.2 显示了我们需要声明的所有类。
列表 1.2
private TcpClient _imapClient;
private NetworkStream _imapNs;
private StreamWriter _imapSw;
private StreamReader _imapSr;
我们将使用 TcpClient
类连接到端口 143 上的邮箱服务器。请查看下面列表 1.3 中的 InitializeConnection()
方法。
清单 1.3
private void InitializeConnection(string hostname, int port)
{
try
{
_imapClient = new TcpClient(hostname, port);
_imapNs = _imapClient.GetStream();
_imapSw = new StreamWriter(_imapNs);
_imapSr = new StreamReader(_imapNs);
Console.WriteLine("*** Connected ***");
Response();
}
catch (SocketException ex)
{
Console.WriteLine(ex.Message);
}
}
InitializeConnection()
方法接受两个参数:邮箱主机名和端口号。实例化一个 TcpClient
,并使用 NetworkStream
类来读写流。如果 _imapClient
对象无法连接到邮箱服务器,将引发 SocketExeption
。
成功连接到邮箱服务器后,服务器将响应一条消息。这通常是“OK”消息,表示您已连接到服务器。下面列表 1.4 中的代码显示了一个非常简单的方法来从邮箱服务器接收数据。
清单 1.4
private string Response()
{
byte[] data = new byte[_imapClient.ReceiveBufferSize];
int ret = _imapNs.Read(data, 0, data.Length);
return Encoding.ASCII.GetString(data).TrimEnd();
}
每次我们将命令发送到邮箱服务器时,都将使用上述 Response()
方法从邮箱服务器获取响应数据。在获得服务器响应后,我们需要使用我们的登录凭据进行登录。
登录命令
我们将发送到邮箱服务器的第一个命令是登录命令。下面列表 1.5 是 AuthenticateUser()
方法,它接受两个参数:用户名和密码。
清单 1.5
public void AuthenticateUser(string username, string password)
{
_imapSw.WriteLine("$ LOGIN " + username + " " + password);
_imapSw.Flush();
Response();
}
上述 AuthenticateUser()
方法向邮箱服务器发送“LOGIN”命令。此命令的结构如下所示。
PREFIX LOGIN USERNAME PASSWORD
每个命令后面都有一个前缀。在 AuthenticateUser()
方法中,使用的前缀是 $ 符号。您可以使用自己的不同前缀,但某些字符(如 *)将不起作用。请注意正在从 AuthenticateUser()
方法调用的 Response()
方法。如前所述,每次我们将命令发送到邮箱服务器时,都需要从服务器获取响应。您可以将响应打印到控制台,但我选择不这样做。
如果提供的用户名和密码成功通过身份验证,服务器将响应一条消息,例如 "$ OK User Loged In"。请注意消息开头的该前缀。此前缀将是您正在使用的前缀。
然而,如果您的登录凭据未通过身份验证,服务器将响应一条消息,例如 "$ NO Login failed: authentication failure"。使用服务器的响应消息,您可以进行自己的异常处理。您可以读取响应的前四个字符,并确定响应是 OK 还是 NO,在这种情况下,您可以通知用户。
成功身份验证后,我们可以执行许多任务,例如获取消息总数。列表 1.6 中的 MailCount()
方法发送一个命令来检查 INBOX 文件夹并从服务器获取消息总数。
清单 1.6
public int MailCount()
{
_imapSw.WriteLine("$ STATUS INBOX (messages)");
_imapSw.Flush();
string res = Response();
Match m = Regex.Match(res, "[0-9]*[0-9]");
return Convert.ToInt32(m.ToString());
}
STATUS 命令接受两个参数。第一个是文件夹,第二个是称为标志的东西。标志 (messages
) 表示您想获取收件箱中消息的总数。如果命令成功执行,服务器将响应类似以下的消息
* STATUS INBOX (MESSAGES 3)
请注意,服务器已响应 INBOX 文件夹中的消息总数;在上面的响应中,消息总数为 3。我们还可以找出收件箱中有多少新消息。列表 1.7 中的 MailUnreadCount()
发送一个 STATUS 命令,但它发送一个 (unseen) 标志。此标志告诉邮箱服务器返回未读消息的总数。
清单 1.7
public int MailUnreadCount()
{
_imapSw.WriteLine("$ STATUS INBOX (unseen)");
_imapSw.Flush();
string res = Response();
Console.WriteLine(res);
Match m = Regex.Match(res, "[0-9]*[0-9]");
return Convert.ToInt32(m.ToString());
}
现在是时候从邮箱服务器获取一些消息了。在我们开始获取任何消息之前,我们必须首先选择要从中检索消息的文件夹。SelectInbox()
方法选择 INBOX 文件夹。选择 INBOX 文件夹后,我们就可以开始获取消息了。下面列表 1.8 向服务器发送 SELECT 命令以选择 INBOX 文件夹。
清单 1.8
public void SelectInbox()
{
_imapSw.WriteLine("$ SELECT INBOX");
_imapSw.Flush();
Response();
}
最后,我们现在可以发送命令以从邮箱服务器获取单个消息。用于获取消息的命令是 FETCH 命令。此命令接受两个参数:第一个是要检索的消息编号,第二个是消息标志的组合。下面列表 1.9 中的 GetMessageHeaders ()
方法接受一个类型为 int
的参数。
清单 1.9
public object GetMessageHeaders(int index)
{
_imapSw.WriteLine("$ FETCH " + index +
" (body[header.fields (from subject date)])");
_imapSw.Flush();
return Response();
}
上述 GetMessageHeaders()
方法向邮箱服务器发送 FETCH 命令,并且仅从选定消息的标题中获取发件人、主题和日期字段。如果您想在列表控件中显示消息以显示所有消息,但不是消息正文,这将非常有用。
要获取电子邮件的正文,您需要向服务器发送 FETCH 命令,但您使用 body[text]
标志。下面列表 2.0 显示了获取消息正文的命令。
列表 2.0
public object GetMessage(int index)
{
_imapSw.WriteLine("$ FETCH " + index + " body[text]");
_imapSw.Flush();
return Response();
}
最后,在完成与邮箱服务器的交互后,我们需要注销。这就像向服务器发送 LOGOUT 命令一样简单。下面列表 2.1 显示了发送注销命令到邮箱服务器的 Disconnect()
方法。
GetMessageHeaders()
和 GetMessage()
方法都将返回未经格式化的响应。您需要格式化来自服务器的响应,并通过处理异常来添加自己的错误处理。为了保持代码简单,我没有添加任何异常处理。