C++ C++ C# C# ASP.NET Security ASP.NET Security ASM ASM Скачать Скачать Поиск Поиск Хостинг Хостинг  
  Программа для работы с LPT портом...
Язык: .NET — ©Alexey...
  "ASP.NET Atlas" – AJAX в исполнении Micro...
Язык: .NET — ©legigor@mail.ru...
  "Невытесняющая" Многопоточность...
Язык: C/C++ — ©...
  01.05.2010 — Update World C++: Сборник GPL QT исходников
  15.12.2007 — Весь сайт целиком можно загрузить по ссылкам из раздела Скачать
Хостинг:
Windows 2003, ASP.NET 2.0
бесплатный и от 80 руб./мес


   Отправить письмо
Кулабухов Артем, Беларусь




 Отправка электронной почты через СМТП сервер / E-mail / Сеть

Отправка электронной почты через СМТП сервер.
           Данная статья  содержит краткую теорию отправки электронной почты
и простой пример реализованный на основе стандартного класса CSocket

Теория СМТП пртокола .
Иногда самый легкий способ посылать сообщение состоит в том, чтобы
передать его почтовому серверу самостоятельно. В обход MFC и прочих
навороченных классов и фунций это сделать немного легче когда не
требуется гибкость приложения именно в плане мульти-документного
программирования.

Простой Протокол Передачи Почты (SMTP) был разработан, чтобы передать
email легко и надежно. Этот протокол был объявлен как RFC-821 в августе
 1982. С тех пор, SMTP, с очень немногими изменениями стал двигателем для
 всех электонных сообщений .
SMTP независим от специфической подсистемы передачи и требует
только надежного ,заказанного канала данных - потока. На Unix системах,
это было осуществлено, используя службы MAIL/SMTP запускаемые посредством
/etc/services файла. Для сервера SMTP используется порт 25 TCP протокола;
следовательно, Вы можете посылать email,  просто соединяясь с гнездом ( сокетом )
потока на системе, которая поддерживает обслуживание службы SMTP.

В этой статье я покажу насколько легко использовать SMTP протокол,
чтобы послать email.

Как Это Работает
    Сервера SMTP основаны на транспортных гнездах (Сокетах).
Использование гнезда (сокета) аналогично использованию телефона:
- поднимаете трубку телефона,
- набераете номер,
- начните говорить,когда кто - то отвечает.

Имеются два типа гнезд , оба из которых двунаправлены:
Поток (TCP) и Datagram (UDP).
SMTP использует TCP протокол гнезда. TCP гнезда работают подобно потоку
байтов; они, как гарантируют, что посланные данные будут получены
точно такими же какими и были посланы.
UDP гнезда - противоположность гнезд потока; они не гарантируют что,
данные продублируются, или даже достигнут их адресата.

Последовательность событий для посылки SMTP сообщений:
- Говорите привет СМТП-серверу.
- Сообщите СМТП-серверу  кто должно получить сообщение.
- Сообщите СМТП-серверу  от кого данное сообщение.
-Сообщите СМТП-серверу сообщение.
-Говорите до свидания СМТП-серверу.

Вы можете общаться с СМТП-сервером, используя команды
HELO, MAIL, RCPT, DATA, и QUIT. Ответы СМТП-сервера состоят
из числового ответа с тремя цифрами, сопровождаемого в соответствии с
человеческим - удобочитаемым сообщением ответа СМТП-сервера. Если
 числовой ответ - меньше чем 400, СМТП-сервер не столкнулся ни с какими
проблемами при обслуживании запроса. Если числовой ответ больший чем 400,
СМТП-сервер сталкнулся с проблемами, для решения которых надо
принимать какое либо решение.

Ниже показан пример сессии с СМТП-сервером.
Связь начинается, с команды HELO. Это сообщает СМТП-серверу,
что я буду использовать SMTP язык (хотя доступна более новая улучшенная
версия языка СМТП-сервера ESMTP). Чтобы активизировать ESMTP,
Вы можете начасть с команды EHLO вместо HELO.) следующим шагом я должен
сообщить СМТП-серверу отправителя почты, потом получателя,
потом само сообщение, затем закрыть связь.

 клиент посылает запрос: HELO somehost.somedomain.com
 СМТП сервер отвечает : 250 OK

 клиент посылает запрос: MAIL FROM:<fazio@danet.com>
 СМТП сервер отвечает : 250 OK

 клиент посылает запрос:  RCPT TO:<someone@someplace.com>
 СМТП сервер отвечает : 250 OK

 клиент посылает запрос:  RCPT TO:<someone.else@elsewhere.com>
 СМТП сервер отвечает : 550 No such user here

 клиент посылает запрос:  RCPT TO:<santa.clause@northpole.com>
 СМТП сервер отвечает : 250 OK

 клиент посылает запрос:  DATA
 СМТП сервер отвечает : 354 Start mail input; end with <CRLF>.<CRLF>
 
 клиент посылает запрос:  All I want for Christmas...
 клиент посылает запрос:  ...etc. etc. etc.
 клиент посылает запрос:  <CRLF>.<CRLF>
 СМТП сервер отвечает : 250 OK

SMTP Команды

Все команды сопровождаются ответами от СМТП-сервера.
ОБРАТИТЕСЬ к RFC-821 для детального объяснения структур ответа.

HELO обычно поcылается в линию отдельно. Это опознает отправителя
     и сообщает СМТП-серверу, что наступающие команды - команды SMTP,
     не ESMTP. Обратите внимание в слове HELO одна буква 'L'

MAIL FROM: <FROM_ADDRESS> объявляет новое сообщение почты, и может
     использоваться внутри существующего сообщения, чтобы показать
     приложение к письму.

RCPT TO: <TO_ADDRESS> определяет всех получателей сообщения.

DATA поcылается в линию отдельно. После того, как Вы посылаете команду
             DATA, СМТП-сервера пошлет назад код 354, `вводите почту и
             завершите сообщение "." с новой линии`.
             После чтения этого начального ответа,
             Вы можете начинать фактическое сообщение.
             Однако, Вы не можете получить другой ответ от СМТП-сервера, пока
             Вы не закончили секцию данных точкой ( . ) на отдельной линии.
             В пределах секции DATA, используйте следующие подзаголовки,
             чтобы форматировать сообщение: FROM:, TO:, CC:, BCC:, DATE:, and SUBJECT:.

QUIT подразумевает то что, СМТП-сервер должен послать "250 ОК" в ответ,
             затем закрыть канал передачи. Ваша программа клиента не должна
             обрывать связь, пока это не получает " 250 ОК " ответ.

RSET (Сброс Сессии) повторно устанавливает текущее сообщение, и очищает
              всего отправителя, получателя, данные, и государственные столы.

VRFY проверяет адрес email и возвращает полностью указанный почтовый ящик.
             Эта команда не добавляет получателей к списку получателя.

HELP объясняет использование команды в человеческой - удобочитаемой форме.

NOOP (Никакого Действия) не делает ничего другого кроме возвращения " 250 ОК " ответа.
 

Интерпретация Ответов
После соединения с обслуживанием(службой) SMTP, ожидайте "220" ответа
от СМТП-сервера. После этого, ожидайте ответ для каждой команды,
посланной СМТП-серверу. Каждый ответ находится в формате трех
числовых цифр, за которыми следует или пробел или дефис, после  которого
идет соответствующее человеческое - удобочитаемое текстовое сообщение для
данного кода. Код с тремя цифрами содержит всю информацию, необходимую
для закодированной обработки ответа. Как предварительно упомянуто,
код, который является меньше чем 400, указывает на успех запроса,
и кодекс большее чем 400 указывает что возникли проблемы.

Ответы - отформатированны следующим образом:
XYZ < пробел или дефис > Сообщение

X имеет значение хороший, плохой или неполный ответ, и может иметь следующие значения:

'1': положительный предварительный ответ (не используемый вообще)
'2': положительный ответ завершения
'3': положительный промежуточный ответ
'4': переходный отрицательный ответ завершения
'5': постоянный отрицательный ответ завершения

'Y' Показывает категорию ответа, и может иметь следующие ценности:
'0': синтаксис
'1': информация
'2': связи
'3': неуказанный в документации
'4': неуказанный в документации
'5': система почты

Z - Показывает подкатегорию ответа.

Дефис указывает, что ответ - часть ответа на много строчный запрос.
Пробел указывает,что ответ - ответ на последнюю строку запроса.
Сообщение текста предназначено для информационных целей только,
и может игнорироваться вашим обработчиком ответа.

Создание приложения отрпавки почты.
                При создании нового проекта выбираем что это будет  MFC AppWizard(exe)
и называться он будет smtp. Выбираем что это Dialog Based приложение, в
следующем окне ставим галочку подтверждения использования Windows Socket.
И далее жмем Next> до упора. Открываем поле редактирования диалога и добиваемся
приблизительно такого произведения искусства.

* Дважды щелкаем по кнопке Send Mail и пишем следующий текст

// определяем MySocket CSocket ; в секции глобальных переменных

void CMy1again1Dlg::OnButton1()
{
 // TODO: Add your control notification handler code here
 
 char responce[1024];
 int res_len;
 char CRLF[2]; CRLF[0]=10;CRLF[1]=13;
// каждая команда для СМТП сервера должна
// заканчиваться <CRLF> т.е. подряд идущими 10 и 13

 char* host = new char[m_host.LineLength()+1];
// выделяем место в памяти для переменной Хоста
 m_host.GetLine(0,host,m_host.LineLength());
//записываем в буфер Хост содержимое ЕдитБокса Host
 host[m_host.LineLength()]=0;
// Хост должен быть ASCIIZ строкой поэтому
// дописываем в конец ноль

 char* recipient = new char[m_recipient.LineLength()];
// выделяем место в памяти для переменной "КОМУ"
 m_recipient.GetLine(0,recipient,m_recipient.LineLength());
//записываем в буфер "КОМУ" содержимое ЕдитБокса
// Recipient
 
 char* from = new char[m_from.LineLength()];
// выделяем место в памяти для переменной "ОТ КОГО"
 m_from.GetLine(0,from,m_from.LineLength());
//записываем в буфер "ОТ КОГО"содержимое ЕдитБокса
// From

 if(!MySocket.Create()) MessageBox("error creating socket","",MB_OK);
// Создаем сокет
 if(!MySocket.Connect(host,25)) MessageBox("error connect to socket","",MB_OK);
// Пытаемся приконектиться к почтовому серверу введенному в поле Хост
 else {
     res_len=MySocket.Receive(responce,sizeof(responce));
    // принимаем ответ от СМТП сервера..
     responce[res_len]=0;
     m_log.ReplaceSel(responce);
    // выводим ответ в окно логов
   }

MySocket.Send("HELO ",5);
MySocket.Send(host,m_host.LineLength());
MySocket.Send(CRLF,2);
// посылаем команду " HELO someposthost.somedomain <CRLF> "
 res_len=MySocket.Receive(responce,sizeof(responce));
 // принимаем ответ от СМТП сервера..
 responce[res_len]=0;
 m_log.ReplaceSel(responce);
 // выводим ответ в окно логов
 
 MySocket.Send("MAIL FROM: <",12);
 MySocket.Send(from,m_from.LineLength());
 MySocket.Send(">",1);
 MySocket.Send(CRLF,2);
// посылаем команду " MAIL FROM: <somebody@someposthost.somedomain>  <CRLF> "
 res_len=MySocket.Receive(responce,sizeof(responce));
 // принимаем ответ от СМТП сервера..
 responce[res_len]=0;
 m_log.ReplaceSel(responce);
 // выводим ответ в окно логов
 

 MySocket.Send("RCPT TO: <",10);
 MySocket.Send(recipient,m_recipient.LineLength());
 MySocket.Send(">",1);
 MySocket.Send(CRLF,2);
// посылаем команду " RCPT TO: <somebody@someposthost.somedomain> <CRLF> "
 res_len=MySocket.Receive(responce,sizeof(responce));
 // принимаем ответ от СМТП сервера..
 responce[res_len]=0;
 m_log.ReplaceSel(responce);
 // выводим ответ в окно логов
 
 MySocket.Send("DATA\n",5);
// посылаем команду " DATA <CRLF> "
 res_len=MySocket.Receive(responce,sizeof(responce));
 // принимаем ответ от СМТП сервера..
 responce[res_len]=0;
 m_log.ReplaceSel(responce);
 // выводим ответ в окно логов
 
//-------------------------------------------------------------
// здесь идет блок посылающий тело сообщения....
 int y = m_body.GetLineCount();
// берем количество строк введенного текста
 for(int i=0;i<y;i++)
 {
 
  int l_lenght = m_body.LineLength(m_body.LineIndex(i));
// вычисляем количество сиволов в текущей строке
// и если строка не пустая то
  if(l_lenght != 0){
       char* curent_line = new char[l_lenght+1];
        // создаем временный буфер
       m_body.GetLine(i,curent_line,l_lenght);
       // записываем во временный буфер очередную строку
       curent_line[l_lenght]=10;

       MySocket.Send(curent_line,l_lenght+1);
       // посылаем серверу очередную строку
 
      delete curent_line;
        //удаляем временный буфер
        }
  else {MySocket.Send("\n",1);}
// если строка пустая  посылаем серверу перевод строки

 }
 
 MySocket.Send(".",1);MySocket.Send(CRLF,2);
// как и положенно конец данных должен обозначаться
// точкой и <CRLF> с новой строки.
 res_len=MySocket.Receive(responce,sizeof(responce));
 // принимаем ответ от СМТП сервера..
 responce[res_len]=0;
 m_log.ReplaceSel(responce);
 // выводим ответ в окно логов

//--------------------------------------------------------------

 MySocket.Send("QUIT\n",5);
//посылаем команду выхода из соединения..
 MySocket.Close();
// закрываем сокет

 delete host;
 delete recipient;
 delete from;
//удаляем временные переменные
}

* Дважды щелкаем по кнопке Quit и пишем следующий текст

void CServerDlg::OnCancel()
{
 // TODO: Add extra cleanup here
 MySocket.Close(); // Закрываем сокет
 CDialog::OnCancel();// Закрывем программу
}
 

Вместо заключения.
* Вот собственно и все, здесь приведены только концепции реализации
клиента  СМТП сервера . Далее выберайте сами подходит это Вам или нет.
Обязательно  посмотрите описания для всех используемых здесь функций.

Пример можно скачать и посмотреть здесь, однако предупреждаю
что особых функций обработки ошибок в примерах нет... (на то он и пример)
Вопросы, просьбы и пожелания отправляйте по адрессу kozloff@dviyka.odessa.net
С ув. и пожеланием успехов Денис Козлов.

Список Литературы.

RFC821, SIMPLE MAIL TRANSFER PROTOCOL, http://www.faqs.org/rfcs.

RFC1651, SMTP Service Extensions, http://www.faqs.org/rfcs.

RFC1893, Enhanced Mail System Status Codes, http://www.faqs.org/rfcs.