请帮助解决我使用 POP3 协议检索电子邮件的问题

发布于 2024-07-17 11:16:13 字数 4425 浏览 8 评论 0原文

我正在使用 C# 和 Microsoft .Net Compact Framework 1.0。 我尝试使用 System.Net.Sockets.NetworkStream 和 TcpClient 类来实现 POP3 协议。 我能够登录并接收电子邮件并使用某些电子邮件服务器保存附件。 但对于某些人来说,我不断遇到问题。

我通过发送 List 命令读取电子邮件的大小。 在某些情况下,我得到的尺寸明显小于实际值。 例如,对于同一封电子邮件:

Actual size: 577860, Returned size: 421096 using Exchange 2007
Actual size: 561005, Returned size: 560997 using Exchange 2003

为什么我总是得不到正确的尺寸? 以下是我正在使用的代码。

电子邮件的大小永远不会与 PopRead 过程末尾的 StringBuilder 的大小匹配。 我无法可靠地读取电子邮件,因为电子邮件大小不可靠,并且 NetworkStreamDataAvailable 属性有时为 false,即使有更多数据可以读取读。

我观察到,当我尝试通过无线方式(使用数据计划)连接到电子邮件服务器时,DataAvailable 属性为 false 的情况比通过 activesync 使用计算机的互联网连接时更常见。

如果有帮助的话,电子邮件服务器是 Exchange 2003 和 Exchange 2007。

private bool POPRead(StringBuilder strBuffer, long lngFetchMailSize)
{
   const int bufferSize = 1024;

    byte[] inb;
    if (enc == null)
    {
        enc = new ASCIIEncoding();
    }

    try
    {
        if (lngFetchMailSize > 0 && lngFetchMailSize < (32 * bufferSize))
        {
            // limit the size of the buffer as the amount of memory
            // on Pocket PC is limited.
            inb = new byte[lngFetchMailSize];
        }
        else
        {
            inb = new byte[bufferSize];
        }
        Array.Clear(inb, 0, inb.Length);
        bool bMoreData = true;
        long iBytesRead = 0L;
        int bytesReadInThisRound = 0;

        int numberOfTimesZeroBytesRead = 0;

        while (bMoreData)
        {
            bytesReadInThisRound = this.nsPOP.Read(inb, 0, inb.Length);
            iBytesRead += bytesReadInThisRound;

            if (bytesReadInThisRound == 0)
            {
                numberOfTimesZeroBytesRead++;
            }
            else
            {//If on a retry the data read is not empty, reset the counter.
                numberOfTimesZeroBytesRead = 0;
            }

            strBuffer.Append(enc.GetString(inb, 0, bytesReadInThisRound));
            Array.Clear(inb, 0, bytesReadInThisRound);
            // DataAvailable sometimes gives false even though there is
            // more to be read.
            bMoreData = this.nsPOP.DataAvailable;
            // Use this number (5), since some servers sometimes give the size
            // of the email bigger than the actual size.
            if ((lngFetchMailSize != 0 && !bMoreData)
                && (iBytesRead < lngFetchMailSize)
                && numberOfTimesZeroBytesRead < 5)
            {
                bMoreData = true;
            }
        }
    }
    catch (Exception ex)
    {
        string errmessage = "Reading email Expected Size: " + lngFetchMailSize;
        LogException.LogError(ex, errmessage, false, "oePPop.POPRead");
        Error = ex.Message + " " + errmessage;
        return false;
    }
    finally
    {
        GC.Collect();
    }
    return true;
}

以下过程用于获取电子邮件的大小:

private long GetMailSize(int index)
{
    StringBuilder strBuffer = new StringBuilder();
    const string LISTError = "Unable to read server's reply for LIST command";
    if ((this.POPServer != null) && (this.nsPOP != null))
    {
        if (!this.POPWrite("LIST " + index))
        {
            return -1L;
        }
        if (!this.POPRead(strBuffer))
        {
            this.Error = LISTError;
            return -1L;
        }
        if (!this.IsOK(strBuffer))
        {
            return -1L;
        }
        string strReturned = strBuffer.ToString();
        int pos1 = strReturned.IndexOf(" ", 3);
        if (pos1 == -1)
        {
            this.Error = LISTError;
            return -1L;
        }
        int pos2 = strReturned.IndexOf(" ", (int)(pos1 + 1));
        if (pos2 == -1)
        {
            this.Error = LISTError;
            return -1L;
        }
        int pos3 = strReturned.IndexOf("\r\n", (int)(pos2 + 1));
        if (pos3 == -1)
        {
            this.Error = LISTError;
            return -1L;
        }
        long mailSize = 0;
        Int64.TryParse(strBuffer.ToString(pos2 + 1, pos3 - (pos2 + 1)).Trim(), out mailSize);
        return mailSize;
    }
    this.Error = NotConnectedError;
    return -1L;
}

希望我提供了解决问题所需的所有信息。 任何帮助或指向正确方向的指示都会有很大帮助。

谢谢,
钱德拉。

I am using C# and Microsoft .Net Compact Framework 1.0. I tried to implement the POP3 protocol using the System.Net.Sockets.NetworkStream and TcpClient classes. I am able to log in and receive email and save the attachments with some email servers. But for some, I keep running into problems.

I read the size of the email message by sending the List <Index> command. In some situations I get a size significantly smaller than the actual value. For example, for the same EMail:

Actual size: 577860, Returned size: 421096 using Exchange 2007
Actual size: 561005, Returned size: 560997 using Exchange 2003

Why don't I ever get the right size? The following the code I am using.

The size of the email never matches the size of the StringBuilder at the end of PopRead procedure. I am not able to read the email reliably because the Email size is not reliable and the DataAvailable property of the NetworkStream is sometimes false, even if there is more data that can be read.

I observed that the DataAvailable property is false more often when I am trying to connect to the email server over the air (using data-plan) than when using the internet connection of the computer through activesync.

If it helps, the email servers are Exchange 2003 and Exchange 2007.

private bool POPRead(StringBuilder strBuffer, long lngFetchMailSize)
{
   const int bufferSize = 1024;

    byte[] inb;
    if (enc == null)
    {
        enc = new ASCIIEncoding();
    }

    try
    {
        if (lngFetchMailSize > 0 && lngFetchMailSize < (32 * bufferSize))
        {
            // limit the size of the buffer as the amount of memory
            // on Pocket PC is limited.
            inb = new byte[lngFetchMailSize];
        }
        else
        {
            inb = new byte[bufferSize];
        }
        Array.Clear(inb, 0, inb.Length);
        bool bMoreData = true;
        long iBytesRead = 0L;
        int bytesReadInThisRound = 0;

        int numberOfTimesZeroBytesRead = 0;

        while (bMoreData)
        {
            bytesReadInThisRound = this.nsPOP.Read(inb, 0, inb.Length);
            iBytesRead += bytesReadInThisRound;

            if (bytesReadInThisRound == 0)
            {
                numberOfTimesZeroBytesRead++;
            }
            else
            {//If on a retry the data read is not empty, reset the counter.
                numberOfTimesZeroBytesRead = 0;
            }

            strBuffer.Append(enc.GetString(inb, 0, bytesReadInThisRound));
            Array.Clear(inb, 0, bytesReadInThisRound);
            // DataAvailable sometimes gives false even though there is
            // more to be read.
            bMoreData = this.nsPOP.DataAvailable;
            // Use this number (5), since some servers sometimes give the size
            // of the email bigger than the actual size.
            if ((lngFetchMailSize != 0 && !bMoreData)
                && (iBytesRead < lngFetchMailSize)
                && numberOfTimesZeroBytesRead < 5)
            {
                bMoreData = true;
            }
        }
    }
    catch (Exception ex)
    {
        string errmessage = "Reading email Expected Size: " + lngFetchMailSize;
        LogException.LogError(ex, errmessage, false, "oePPop.POPRead");
        Error = ex.Message + " " + errmessage;
        return false;
    }
    finally
    {
        GC.Collect();
    }
    return true;
}

The following procedure is used to get the size of the email message:

private long GetMailSize(int index)
{
    StringBuilder strBuffer = new StringBuilder();
    const string LISTError = "Unable to read server's reply for LIST command";
    if ((this.POPServer != null) && (this.nsPOP != null))
    {
        if (!this.POPWrite("LIST " + index))
        {
            return -1L;
        }
        if (!this.POPRead(strBuffer))
        {
            this.Error = LISTError;
            return -1L;
        }
        if (!this.IsOK(strBuffer))
        {
            return -1L;
        }
        string strReturned = strBuffer.ToString();
        int pos1 = strReturned.IndexOf(" ", 3);
        if (pos1 == -1)
        {
            this.Error = LISTError;
            return -1L;
        }
        int pos2 = strReturned.IndexOf(" ", (int)(pos1 + 1));
        if (pos2 == -1)
        {
            this.Error = LISTError;
            return -1L;
        }
        int pos3 = strReturned.IndexOf("\r\n", (int)(pos2 + 1));
        if (pos3 == -1)
        {
            this.Error = LISTError;
            return -1L;
        }
        long mailSize = 0;
        Int64.TryParse(strBuffer.ToString(pos2 + 1, pos3 - (pos2 + 1)).Trim(), out mailSize);
        return mailSize;
    }
    this.Error = NotConnectedError;
    return -1L;
}

Hope I gave all the information needed for solving the issue. Any help or pointer in the right direction would be a lot of help.

Thanks,
Chandra.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

愁以何悠 2024-07-24 11:16:13

您的错误可能在于按照您的方式使用 ASCIIEncoder。

来自 MSDN

需要转换的数据,例如data
从流中读取,可用
仅在连续块中。 在这个
情况,或者如果数据量是这样
大到需要分成
较小的块,应用程序应该
使用解码器或编码器
由 GetDecoder 方法提供或
分别是 GetEncoder 方法。

由于您一次解码一点点,因此可能错误地解码了部分流。

我将更改您的代码以使用 解码器,或者立即读取整个消息,然后使用 GetString() 成员对其进行解码。

作为额外的健全性检查,您可以使用 RETR所指定的消息大小。 返回并查看它是否与 LIST 返回的内容匹配。 如果它们不匹配,至少我会选择 RETR 返回的内容。

Your error may lie in using the ASCIIEncoder the way you are.

From MSDN:

Data to be converted, such as data
read from a stream, can be available
only in sequential blocks. In this
case, or if the amount of data is so
large that it needs to be divided into
smaller blocks, the application should
use the Decoder or the Encoder
provided by the GetDecoder method or
the GetEncoder method, respectively.

Since you are decoding a little bit at a time, it is possible it is decoding portions of the stream incorrectly.

I would change your code to use a decoder, or to read the entire message at once, then decode it with the GetString() member.

As an additional sanity check you could use the message size that RETR <index> returns and see if it matches what LIST returns. If they don't match, I at least, would go with what RETR returns.

秋叶绚丽 2024-07-24 11:16:13

POP3 是一个棘手的协议。 需要处理如此多不同的服务器,其中许多都有自己晦涩难懂的怪癖。 如果这是一个生产应用程序,我会认真考虑购买经过彻底测试的第三方组件。

POP3 is a tricky protocol. So many different servers to deal with, many with their own obscure quirks. If this is a production app, I would seriously consider buying a 3rd party component that's been thoroughly tested.

千年*琉璃梦 2024-07-24 11:16:13

由于电子邮件应以句点结尾,因此我会进行此比较以终止循环。

而不是

 if ((lngFetchMailSize != 0 && !bMoreData)
       && (iBytesRead < lngFetchMailSize)
       && numberOfTimesZeroBytesRead < 5)
  {
      bMoreData = true;
  }

我会把它写成

  if(!bMoreData 
    && strBuffer.ToString(strBuffer.Length - 5, 5) != "\r\n.\r\n")
  {
       bMoreData = true;
  }

Since an email should end with a period, I would do this comparison for termination of the loop.

Instead of

 if ((lngFetchMailSize != 0 && !bMoreData)
       && (iBytesRead < lngFetchMailSize)
       && numberOfTimesZeroBytesRead < 5)
  {
      bMoreData = true;
  }

I would write it as

  if(!bMoreData 
    && strBuffer.ToString(strBuffer.Length - 5, 5) != "\r\n.\r\n")
  {
       bMoreData = true;
  }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文