Пожалуйста, помогите с моими проблемами при получении электронной почты по протоколу POP3 - PullRequest
2 голосов
/ 18 апреля 2009

Я использую C # и Microsoft .Net Compact Framework 1.0. Я попытался реализовать протокол POP3, используя классы System.Net.Sockets.NetworkStream и TcpClient. Я могу войти в систему, получать электронную почту и сохранять вложения на некоторых почтовых серверах. Но для некоторых я продолжаю сталкиваться с проблемами.

Я прочитал размер сообщения электронной почты, отправив команду List <Index>. В некоторых ситуациях я получаю размер, значительно меньший, чем фактическое значение. Например, для того же EMail:

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

Почему я никогда не получаю правильный размер? Следующий код, который я использую.

Размер электронного письма никогда не совпадает с размером StringBuilder в конце PopRead процедуры. Я не могу прочесть письмо надежно, потому что размер письма ненадежен, а свойство DataAvailable NetworkStream иногда ложно, даже если есть еще данные, которые можно прочитать.

Я заметил, что свойство DataAvailable чаще ложно, когда я пытаюсь подключиться к почтовому серверу по беспроводной сети (используя тарифный план), чем при использовании интернет-соединения компьютера через 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;
}

Надеюсь, я дал всю информацию, необходимую для решения проблемы. Любая помощь или указатель в правильном направлении будет большой помощью.

Спасибо
Chandra.

Ответы [ 3 ]

1 голос
/ 18 апреля 2009

Ваша ошибка может заключаться в использовании ASCIIEncoder таким, какой вы есть.

С MSDN :

Данные для преобразования, такие как данные читать из потока, может быть доступно только в последовательных блоках. В этом случай, или если количество данных так большой, что его нужно разделить на меньшие блоки, приложение должно используйте декодер или кодировщик предоставляется методом GetDecoder или метод GetEncoder соответственно.

Поскольку вы декодируете немного за раз, возможно, это неправильно декодирует части потока.

Я бы изменил ваш код для использования декодера или для чтения всего сообщения сразу, а затем расшифровал его с помощью члена GetString ().

В качестве дополнительной проверки работоспособности вы можете использовать размер сообщения, которое возвращает RETR , и посмотреть, соответствует ли оно тому, что возвращает LIST. Если они не совпадают, я бы, по крайней мере, пошел бы с тем, что возвращает RETR.

0 голосов
/ 21 апреля 2009

Поскольку электронное письмо должно заканчиваться точкой, я бы сделал это сравнение для завершения цикла.

Вместо

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

Я бы написал это как

  if(!bMoreData 
    && strBuffer.ToString(strBuffer.Length - 5, 5) != "\r\n.\r\n")
  {
       bMoreData = true;
  }
0 голосов
/ 18 апреля 2009

POP3 - сложный протокол. Так много разных серверов для работы, многие со своими непонятными причудами. Если это производственное приложение, я бы серьезно подумал о покупке стороннего компонента, который был тщательно протестирован.

...