Версия для печати
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум на Исходниках.RU > .NET FAQ > Реализация протокола POP3


Автор: Pit-Bul 06.08.06, 12:43
POP3 (Post Office Protocol version 3 - протокол приёма почты) нужен для приёма почты с почтового сервера. Использует 110
порт, по которому посылаются команды и текст письма. На написание этой статьи меня сподвигло то что после долгих поисков
в сети примера, показывающего как работать с POP3 протоколом с помощью VB.NET так и не привело к успеху, но благодаря
FlyDead'у, который поделился примером на С# (с хорошими коментариями) мне всетаки удалость приконектиться к 110 порту
своего почтового сервера и забрать оттудого свою почту. Далее я приведу код любезно предоставленный FlyDead'ом и подробно
опишу код на VB.NET

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    Public void CheckMail()
    {
    NetworkStream ns;
                StreamReader sr;  //два потока для чтения из потока
                StreamWriter sw;  //и для записи в поток
                String response; //для хранения результата запроса
                
                TcpClient tcpClient = new TcpClient(); //создаем клиент
                try
                {
                    tcpClient.Connect(cConfig.ServerPop,Convert.ToInt32(cConfig.PortPop));
                }
                catch(SocketException sExc)
                {
                    MessageBox.Show("Соединение неустановлено \n"+"Проверьте настройки POP3 сервера и номер порта \n"+sExc.Message);
                    Return;
                }
                catch(Exception eX)
                {
                    MessageBox.Show("Соединение неустановлено \n"+eX.Message);
                    Return;
                }
                ns = tcpClient.GetStream(); //запускаем потоки
                sr = new StreamReader(ns);
                sw = new StreamWriter(ns);
                
                response = sr.ReadLine();
                sw.WriteLine("User " + cConfig.UserPop); //посылаем юзера
                sw.Flush();  
                response = sr.ReadLine(); //опять читаем что вернул сервер
                
                If (response.Substring(0,3) == "-ER") //если вернул сообщение об ошибке
                {
                    MessageBox.Show("Неверный логин","Ошибка аутентификации",MessageBoxButtons.OK,MessageBoxIcon.Error);
                    Return;
                }
                sw.WriteLine("Pass " + cConfig.PasswordPop); //Послылаем пароль
                sw.Flush(); //очистка
     
                response = sr.ReadLine(); //опять читаем
                If (response.Substring(0,4) == "-ERR") //если не ошибка то идем дальше
                {
                    MessageBox.Show("Неверный пароль","Ошибка аутентификации",MessageBoxButtons.OK,MessageBoxIcon.Error);
                    Return;
                }
                sw.WriteLine("stat");
                sw.Flush();
                response = sr.ReadLine();
                String[] nummess = response.Split(' ');
                Int totmessages;
                totmessages = Convert.ToInt16(nummess[1]);
                If (totmessages > 0)
                {
                    notifyIcon1.Icon = (System.Drawing.Icon)resources.GetObject("2.ico");
                    MessageBox.Show(Convert.ToString(totmessages)+" новых сообщений");
                }
                Else
                {
                    notifyIcon1.Icon = (System.Drawing.Icon)resources.GetObject("1.ico");
                }
                tcpClient.Close();
            }





А теперь все то же самое на VB.NET
1. Шаг первый, надо соединиться с сервером, вот код который это делает:

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    Dim client As Net.Sockets.TcpClient
        Dim sr As IO.StreamReader
        Dim sw As IO.StreamWriter
        Dim ns As Net.Sockets.NetworkStream
        Dim otvet As String
        'соединяемся с сервером
            client = New Net.Sockets.TcpClient
            Try
                client.Connect(server, 110)
            Catch e As Exception
                MessageBox.Show("Соединение не установленно" + e.ToString, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly, False)
                Return
            End Try
            ns = client.GetStream
            sr = New IO.StreamReader(ns, Encoding.ASCII, True) 'это то что отправляет наши команды на сервер
            sw = New IO.StreamWriter(ns)                       'а это то что читает ответы сервера
            otvet = sr.ReadLine()                              'это наш ответ от сервера


На это нам сервер должен ответить +ОК [какойто текст]. В конце каждой отправленной сервером строки
обязательно символы перехода на новую строку(chr(13) & chr(10))


2.Второй шаг, после утвердительного ответа сервера отправляем имя пользователя

user [имя] & chr(13) & chr(10)

На что сервер нам может ответить следующее:

+OK [какойто текст]
- такой пользователь существует, продолжайте
-ERR [какойто текст] - ошибка, либо такого пользователя нет

Вот код который это делает:


<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
            sw.WriteLine("User " & user & Chr(13) & Chr(10))
            sw.Flush()
            otvet = sr.ReadLine()
            If otvet.Substring(0, 3) = "-ER" Then _
           MessageBox.Show("Неверный логин", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error,_
    MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly, False)



3.Далее посылаем серверу пароль

pass [****] & chr(13) & chr(10) Вместо **** указывается пароль.

На что сервер нам опять ответит следующим:
+OK [какойто текст] - пароль верный, продолжайте
-ERR [какойто текст] - ошибка, либо неверный пароль

Вот код который это делает:

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
             'отправляем пароль
            sw.WriteLine("Pass " & pass & Chr(13) & Chr(10))
            sw.Flush()
            otvet = sr.ReadLine
            If otvet.Substring(0, 4) = "-ER" Then _
           MessageBox.Show("Неверный пароль", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error,_
    MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly, False)



4.Теперь можно определить количество новых писем на сервере, и их общий объём, для этого отправим
stat

Ответом будет
+OK [кол-во] [объем]

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
        Dim nummess As Array
        Dim totmessages As Integer
            sw.WriteLine("stat")
            sw.Flush()
            otvet = sr.ReadLine()
            nummess = otvet.Split(" ")
            totmessages = CInt(nummess(1))



5.Для того чтобы определить индексы писем и объем каждого по отдельности, отправляем
list

Сервер ответит списком с номером и объемом письма
+OK 2 messages 320
1 120
2 200
.

Список оканчивается переходом на новую строку и знаком "."


<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
        Dim i As Integer
        Dim MessageArr() As String
            'получаем идеинтификаторы писем
            sw.WriteLine("list")
            sw.Flush()
            otvet = sr.ReadLine()
            Debug.Print(otvet)
            myArr = otvet.Split(" ")
            ReDim MessageArr(totmessages - 1)
            For i = 0 To CInt(myArr(1)) - 1
                MessageArr(i) = (sr.ReadLine())
            Next



6.Теперь мы можем производить разные операции с письмами
dele [x]
Удаление сообщения номер x. Изменения вступают в силу только после корректного
закрытия соединения.

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
            sw.WriteLine("dele x") 'х это идеинтификатор письма
            sw.Flush()
            otvet = sr.ReadLine()



Возможные ответы:
+OK [какойто текст] - письмо удалено
-ERR [какойто текст] - нет письма с таким идеинтификатором

retr [x]
Приём сообщения номер x. Оно оканчивается переходом на новую строку и знаком "."

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
            'получаем письма
            Dim x As Integer = 0
            sr = New IO.StreamReader(ns, Encoding.Default, True) 'Encoding.Default вот чего не хватало для чтения руских букв
            For i = 0 To (totmessages - 1)
                sw.WriteLine("retr " & (i + 1))
                sw.Flush()
                FileOpen(1, Application.StartupPath & "\mails\Mail" & MessageArr(i) & ".txt", OpenMode.Append, OpenAccess.Write)
                Do While otvet <> "."
                    otvet = sr.ReadLine
                    Debug.Print(otvet)
                    Print(1, otvet & vbNewLine)
                Loop
                FileClose(1)
            Next i


Возможные ответы:
+OK [какойто текст] - вот ваше письмо
-ERR [какойто текст] - нет такого письма

Примеры:

+OK 120 octets
[заголовок письма]
[текст письма]
.




Дополнительные POP3 команды

Следующие дополнительные команды дают вам большую свободу при работе с
сообщениями:

Команда: TOP [сообщение] [n]

Аргументы: [сообщение] - номер сообщения [n] - положительное число
(обязательный аргумент)

Описание: Если ответ сервера положительный, то после него он передаёт
заголовки сообщения и указанное кол - во строк из тела сообщения.

Возможные ответы:

+OK top of message follows
-ERR no such message

Примеры:

C: TOP 1 10 S: +OK
S: <здесь POP3 сервер передаёт заголовки
первого сообщения и первые 10-ть строк из тела сообщения.>
S: . ...
C: TOP 100 3
S: -ERR no such message


Команда: UIDL [сообщение]
Аргументы: [сообщение] - номер сообщения (необязательный аргумент).

Описание: Если был указан номер сообщения, то сервер выдаёт уникальный
идентификатор для этого сообщения. Если аргумент не был передан, то
идентификаторы перечисляются для всех сообщений, кроме помеченных для
удаления.

Возможные ответы:
+OK unique-id listing follows
-ERR no such message


Примеры:

C: UIDL S: +OK
S: 1 whqtswO00WBw418f9t5JxYwZ
S: 2 QhdPYR:00WBw1Ph7x7
S: . ...
C: UIDL 2
S: +OK 2 QhdPYR:00WBw1Ph7x7 ...
C: UIDL 3
S: -ERR no such message, only 2 messages in maildrop



Команда:loop
Эта команда просто говорит серверу, что мы ещё висим на порту, чтобы он не закрывал соединение
после длительной паузы.

Команда:quit
Закрытие соединения.

Эта тема была разделена из темы "Наполнение и развитие FAQ"

Автор: zipa455 22.06.09, 03:00
Цитата Pit-Bul @
3.Далее посылаем серверу пароль

pass [****] & chr(13) & chr(10) Вместо **** указывается пароль.

На что сервер нам опять ответит следующим:
+OK [какойто текст] - пароль верный, продолжайте
-ERR [какойто текст] - ошибка, либо неверный пароль

Вот код который это делает:

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
             'отправляем пароль
            sw.WriteLine("Pass " & pass & Chr(13) & Chr(10))
            sw.Flush()
            otvet = sr.ReadLine
            If otvet.Substring(0, 4) = "-ER" Then _
           MessageBox.Show("Неверный пароль", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error,_
    MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly, False)

Сегодьня такой способ проверки пароля на сервере pop.mail.ru не работает. Выдаёт ошибку, что пароль неверный, а в переменной "otvet" текст такой: -ERR Unknown command:"". Жаль.

Автор: Pit-Bul 06.07.09, 08:45
zipa455, возможно сервер использует какой либо шифрованный метод аутеинтификации

Автор: SmartX 16.07.09, 10:28
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
            If otvet.Substring(0, 4) = "-ER"

В любом случае вернёт false. Независимо от того что будет в переменной otvet.
Правильно будет либо так:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
            If otvet.Substring(0, 3) = "-ER"

либо так:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
            If otvet.Substring(0, 4) = "-ERR"


Кстати такой код отлично работает. Проверяно только что.
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
            private void button2_Click(object sender, EventArgs e)
            {
                NetworkStream ns;
                StreamReader rs;
                StreamWriter ws;
                String response;
                String userName = "user@mail.ru";
                String pass = "password";
     
                TcpClient client = new TcpClient();
                try
                {
                    client.Connect("pop.mail.ru", (Int32)110);
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Соединение не установлено! \n" + ex.Message, "Ошибка соединения!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
                ns = client.GetStream();
                rs = new StreamReader(ns);
                ws = new StreamWriter(ns);
                response = rs.ReadLine();
                ws.WriteLine("User " + userName);
                ws.Flush();
                response = rs.ReadLine();
                if (response.Substring(0, 3) == "-ER")
                {
                    MessageBox.Show("Неверный логин", "Ошибка аутентификации", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    client.Close();
                    return;
                }
                ws.WriteLine("Pass " + pass);
                ws.Flush();
                response = rs.ReadLine();
                if (response.Substring(0, 3) == "-ER")
                {
                    MessageBox.Show("Неверный пароль", "Ошибка аутентификации", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    client.Close();
                    return;
                }
                MessageBox.Show("Соединение установлено!");
                client.Close();
            }

Автор: ___BornInUssr___ 13.10.09, 19:15
Ребята, вот такой вопрос: Как автоматически определить настройки почтового сервера?
Пример: в Outlook вводим только логин, пароль, почтовый адрес и все. А Outlook сам автоматом определяет pop3.domain и порт.
Определяет даже не стандартные порты и необходимость использования SSL.

Автор: 2shae 23.02.12, 10:43
коллеги с pop.gmail.com и порт=995 не работает, никто с gmail не сделал?

Powered by Invision Power Board (https://www.invisionboard.com)
© Invision Power Services (https://www.invisionpower.com)