почтовая программа - своими руками!☛Java, JavaScript ✎ |
Для успешного написания почтового клиента на Java для начала необходимо изучить протоколы на которых общаются клиент и сервер. Освоить написание почтового клиента на Java сможет каждый кто хоть как то знаком с этим языком.
Написание программ на Java представляет собой весьма интересное занятие, поскольку можно абстрагироваться от многих деталей, с которыми неизбежно сталкиваются пользователи Cи++, Паскаля и других языков программирования третьего поколения. С самого начала специалисты корпорации Sun задумали Java как язык, стирающий разницу между разрозненными компьютерами и сетями, и, видимо, поэтому значительная часть библиотеки классов Java абстрагирует сетевые операции и транспортные протоколы.
Простейшая Java-программа, принимающая почту с сервера, - это по силам каждому.
Передача данных
Прежде чем написать почтовый клиент, неплохо было бы изучить язык, на котором общаются почтовый клиент и сервер, - протокол POP3 (Post Office Protocol). Именно с помощью этого протокола в большинстве случаев принимаются почтовые сообщения такими популярными почтовыми программами, как Microsoft Outlook и Express Netscape Messanger. Для начала примите к сведению, что все почтовые операции на нижнем уровне производятся с применением транспортного протокола TCP. Сокет сервера передает данные в формате POP3 TCP-пакетами в порт 110 (более ранняя вторая версия протокола POP использовала порт 109).
Кроме того, необходимо знать, что команды от клиента к серверу пересылаются в виде текстовых строк, завершающихся парой символов "перевод каретки" (CR) и "перевод строки" (LF). Получив такое сообщение, почтовый сервер приступает к тяжелой работе: либо отвечает на ваши команды, либо пересылает сообщения, имеющиеся в вашем почтовом ящике. Вот здесь начинается настоящее мучение, ибо те, кто разрабатывал протокол POP3, променяли удобство использования на простоту реализации, не подумав о программистах, которым с ним работать.
Формат обмена данными
Команды, которые вы отправляете серверу, делятся на две категории: без параметров и содержащие параметры. Если простые команды без параметров состоят всего из одного слова, то в команды с параметрами добавляются текстовые параметры (или цифровые параметры, преобразованные в текстовую форму и разделенные пробелами). Каждый параметр может иметь длину до 40 символов. В качестве примера простой команды можно привести команду "STAT", которая запрашивает состояние вашего почтового ящика. Примером команды с параметром может служить команда "USER Mitrich", сообщающая серверу, что к нему подключается пользователь с именем Mitrich. Прежде чем отправиться дальше, загляните в таблицу и ознакомьтесь со всеми командами, которые описывает протокол POP3.
Возвращаемые сообщения могут быть двух видов: одно- и многострочные. В первом случае сервер возвращает одну строку; она начинается с признака статуса +OK или -ERR, говорящих об успехе или неудаче выполнения последней команды. Следом за признаком статуса сервер передает полезную информацию различного назначения. Завершается строка символами 'r' и 'n'. Если же возвращаемое сообщение многострочное, то оно передается строка за строкой, каждая из которых завершается 'r' и 'n', а в качестве признака окончания передачи используется строка из единственного символа '.' (точка), также оканчивающаяся символами возврата каретки и перевода строки.
Класс POPClient
Ядро почтового клиента - это класс POPClient, реализованный на языке Java (см. листинг 1). Он предоставляет пользователю несколько полезных функций для управления посылкой команд серверу и получения ответа от последнего. Класс POPClient вмещает в себя два внутренних класса - POPCommand и POPResponse. Такого разбиения единого, казалось бы, класса на подклассы требует сама суть объектно-ориентированного программирования: каждая сущность должна быть реализована как отдельный класс. А поскольку и POPCommand и POPResponse являются вспомогательными по отношению к главному классу POPClient, то они выполнены не как независимые классы, а как классы внутренние. Средства пакета JDK 1.1 предусматривают решение подобных задач.
Внутренний класс POPCommand
Класс POPCommand, как вы, наверное, уже догадались, является инкапсуляцией команды протокола POP3. Для его создания нет конструктора, поэтому этим занимается компилятор Java. С помощью объектов этого класса формируются команды для посылки серверу и производится собственно посылка команды. Кроме того, обязанностью этого класса является чтение ответа сервера на посланную ранее команду. Самую черную работу на нижнем уровне выполняют следующие методы:
public boolean user(String name); public boolean pass(String password); public boolean quit(); public boolean dele(int number); public boolean rset(); public boolean list(int number); public boolean retr(int number).
Нетрудно догадаться, что это всего лишь оболочки для посылки команд с аналогичными именами. Внутри этих методов происходит стандартная обработка: имеющийся аргумент проверяется, передается вспомогательному методу transactCommand() и затем возвращается в виде true (команда прошла и обработана сервером) или false (произошла ошибка). Когда же сервер прислал вместо вразумительного ответа "тарабарщину", происходит возбуждение исключения под названием POPException (о нем ниже в разделе "Класс POPException").
Метод transactCommand принимает текстовую строку, которая соответствует посылаемой команде, передает ее методу sendCommand(), затем читает ответ сервера с помощью метода readCommandResponse(). В заключение вызывается метод isSucceed() внутреннего класса POPResponse. Этот метод возвращает результат последней транзакции.
Если работа sendCommand() достаточно понятна, то работа метода readCommandResponse() требует некоторого пояснения. В начале своей работы этот метод создает объект класса StringBuffer для временного хранения данных. Потом из потока ввода, подключенного к сокету, командой ir.readLine() читается строка ответа, которая добавляется к буферу строки методом append(). В момент окончания работы метода значение буфера преобразуется в строку методом toString() и записывается во внутреннее хранилище класса POPResponse методом setBuff().
Но не все так просто, как может показаться с самого начала. Дело в том, что читать данные из потока сокета можно, а вот установить момент, когда они считаны до конца, нельзя. И документация в этом деле, увы, ничем не поможет. Там рекомендовано читать строки из сокета до тех пор, пока возвращаемое значение не будет равно null. Но уверяю вас, этого момента вы не дождетесь. Точно так же вы не сможете воспользоваться методом read() для чтения из-за того, что и он блокируется при достижении конца передачи данных. Со стороны это выглядит как зависание, но на самом деле поток, в котором вызывается метод чтения данных, просто переходит в состояние ожидания очередной порции информации. Поэтому мы идем на компромисс и в дополнение к методу readCommandResponse() реализуем метод readMessage(), который предназначается лишь для считывания многострочного почтового сообщения. Конец передачи данных мы определяем по строке, состоящей из одного символа точки ".". Метод readMessage() вызывается из командного метода retr() и, вместо того чтобы, как другие команды, сделать вызов метода transactCommand(), выполняет следующие три строки на Java:
sendCommand("RETR " + Integer.toString(number)); readMessage(); return response.isSucceed();
Обратите внимание, что многие методы класса POPCommand объявлены как private, чтобы предотвратить прямой доступ к ним.
Внутренний класс POPResponse
Следующий внутренний класс POPResponse отвечает за хранение полученных от сервера данных и предоставление удобного интерфейса для их обработки. Все данные, принятые от сервера, сохраняются методом setBuff() в переменной buff, которая есть не что иное, как ссылка на строку класса String. Для вспомогательных нужд есть метод getBuff, с помощью которого можно получить содержимое хранилища в том виде, в каком оно было получено от почтового сервера.
Для предварительной обработки данных вы можете вызывать еще два полезных метода: cutOffStatus() и getServerComment(). Первый из них берет содержимое временного хранилища, отрезает от него признак статуса (+OK или -ERR) и возвращает оставшуюся строку. Второй метод возвращает текстовое сообщение, которое было передано POP-сервером сразу за признаком статуса. Следует, однако, отметить, что применять этот метод можно лишь после того, как серверу была передана команда, следом за которой сервер непременно пришлет сообщение (см. таблицу), иначе возвращенная методом getServerComment() строка будет бессмысленна.
Настал черед главного класса POPClient, с которым нам нужно разобраться. Сразу после объявления его самого описывается странная на первый взгляд переменная debug. Значение этой переменной управляет включением и выключением вывода отладочных сообщений в поток System.out. Если установить значение true, то специально написанный метод logText() будет посылать любую текстовую строку, которую вы ему передадите в окно консоли. После окончания отладки класса, вы можете установить debug в состояние false, и ваши строки, передаваемые logText(), уже не будут выводиться. Мало того, компилятор автоматически уберет посылки текста в окно консоли, оптимизируя код. Данная возможность походит на директивы условной компиляции препроцессора языков Cи и Cи++. О другой функции метода logText() мы поговорим чуть позже.
В начале класса POPClient объявляется несколько полей, имеющих следующее назначение:
POPCommand command - ссылка на объект внутреннего класса POPCommand; POPResponse response - ссылка на объект внутреннего класса POPResponse; Socket socket - ссылка на сокет, связанный с почтовым сервером; BufferedReader ir - объект, читающий данные из потока ввода сокета; PrintWriter ow - объект, записывающий данные в поток вывода сокета; PrintWriter log - объект, записывающий данные в файл протокола; Vector messages - хранилище полученных почтовых сообщений; boolean logEnabled - флаг, разрешающий или запрещающий вывод текста в файл протокола.
В конструкторе по умолчанию производится создание новых экземпляров объектов внутренних классов POPCommand и POPResponse для дальнейшей работы. Если вам требуется протоколировать работу вашего почтового клиента, то воспользуйтесь другим конструктором, принимающим имя файла, который будет служить файлом протокола. Сначала этот конструктор вызовет конструктор по умолчанию, инициализируя таким образом внутренние классы, а затем создаст поток вывода данных в файл. Если таковой поток не удается создать, то возникает исключение ввода-вывода, которое мы перехватываем, и устанавливаем флаг разрешения протокола в положение "отключено" (false).
Для забывчивых программистов в классе предусмотрен финализатор - метод finalize(), вызываемый сборщиком мусора в момент завершения работы класса. Финализатор вызывает метод disconnect(), отключающий клиента от почтового сервера, если программист по каким-либо причинам не вызывал метод disconnect().
Для соединения с POP-сервером создаваемый нами класс оснащен методом connect(), который достаточно прост. На первом этапе connect() создает сокет для присоединения к серверу с заданным именем и портом. Если сервер с именем hostName не существует, то после некоторого ожидания будет возбуждено исключение UnknownHostException. То же произойдет при возникновении проблем с вводом-выводом данных - будет сгенерировано исключение IOException. Мы перехватываем эти исключения и генерируем свое - POPException. В качестве параметра конструктора задается константа, определяющая вид исключения, которое мы хотим возбудить.
После создания сокета, мы вызываем его методы getInputStream()и getOutputStream(), чтобы получить ссылки на потоки ввода и вывода сокета, через которые мы будем посылать и получать данные. Для удобства работы полученные ссылки преобразуются в экземпляры объектов классов BufferedReader и PrintWriter, пришедших на смену морально устаревшим классам потоков BufferedInputStream и PrintStream. Остается считать ответ сервера:
command.readCommandResponse();
и определить, произошло ли соединение:
return response.isSucceed();
Для отключения от сервера существует метод disconnect(). Он работает довольно прямолинейно: посылает серверу команду QUIT и поочередно закрывает потоки протокола сокетов, а затем и сам сокет.
Метод logText(), который косвенно упоминался выше, служит для выполнения двух функций: запись строки, полученной через параметр text, в файл протокола, если протоколирование разрешено. Эти же данные пересылаются в окно консоли в том случае, если флаг debug задан как true.
Для получения доступа к почте необходимо вызвать метод login(), посылающий имя пользователя и пароль почтовому серверу. Если какой-либо из параметров неверен, то происходит генерация исключения POPException.
Другие материалы по теме:
- Веб-дизайн для нейросетей: Как ваши страницы видит искусственный интеллект- Java: русские буквы и не только...
- ZIRP: Эпоха нулевой процентной ставки закончилась — как это влияет на IT-стартапы
- Web 3.0: Реальность или очередной хайп?
- революция java
