65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.53/5 (24投票s)

2008年9月23日

GPL3

6分钟阅读

viewsIcon

191604

downloadIcon

7775

本文将介绍如何使用 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() 方法都将返回未经格式化的响应。您需要格式化来自服务器的响应,并通过处理异常来添加自己的错误处理。为了保持代码简单,我没有添加任何异常处理。

© . All rights reserved.