Сегодня речь вновь пойдет о замечательном языке серверных приложений — о PHP. Я покажу, как на нем можно написать, ни много ни мало, программу чтения USENET-новостей.
Что такое USENET? Это бесконечное пространство для общения (подробнее см. статью Валерия Аксака «Интернет-андеграунд + ФИДО = Usenet» в МК 39(158)). Чтобы подробно узнать, «как туда попасть», можно открыть справку Outlook Express и зайти в раздел «Работа с группами новостей». Я же дам краткую характеристику. В USENET есть огромнейшее количество групп новостей (как их называют, эхо-конференций, форумов). Стоит заметить, что «новости» — это лишь формальное название, которое не имеет отношения к УНІАН. Все группы строго разделены по тематике. Пользователи, подписываясь на определенный форум, получают возможность читать письма других участников и самостоятельно отправлять письма в группу. Поскольку новости читает большое количество людей, вы можете быстро получить ответ на возникший вопрос и так же быстро испытать моральное удовлетворение, помогая найти ответы другим.
Работает все это чудо по протоколу NNTP (Network News Transfer Protocol — Протокол Передачи Сетевых Новостей). Полное описание данного протокола можно найти по адресу .
Сейчас мы «наскриптим» скрипт, который будет представлять из себя программу для чтения конференций (ньюсридер, как ее называют «кулхацкеры»).
Перед тем как приступить к практике — немного теории. Весь процесс работы с NNTP-сервером заключается в том, чтобы:
1. Открыть соединение, или, как говорят, TCP Socket (socket — розетка);
2. При необходимости идентифицироваться на сервере — передать логин и пароль;
3. Получить информацию о конкретной группе или нескольких группах. Что именно? Список групп, доступных на сервере, количество новых статей (или их общее количество) в выбранной группе.
4. При необходимости можно загрузить все сообщения или только их заголовки (поля «От:», «Тема:» и т.д.). Можно запостить письмо.
5. В конце всех действий нужно закрыть соединение (сокет).
Вот, в принципе, и все действия, к которым сводится процесс общения с сервером. Но проще сказать, чем сделать.
Теперь надо решить, чего мы ожидаем от нашего скрипта.
Поскольку скрипт, который я буду здесь описывать — простенький (но функционирующий) пример, он будет всего лишь уметь писать и отправлять письма в конференцию. Приступим к созданию html-страницы, из которой будет вызываться наш скрипт.
<title>USENET — Клиент</title> <form action="nntpclient.php"> Сервер: <input type="text" name="ServerName"><br> Логин: <input type="text" name="UserLogin"><br> Пароль: <input type="password" name="UserPass"><br> Имя группы новостей: <input type="text" name="GroupName"><br> Получить последних <select name="MessLimit"> <option>5</option> <option>10</option> <option>15</option> </select> сообщений<p> Информация для отправляемого сообщения:<br> От: <input type="text" name="FromName"><br> Тема: <input type="text" name="Subject"></p><p> Текст сообщения:<br> <textarea name="PostedMess" rows="10"> </textarea></p><p> <input type="submit" value="Готово"><input type="reset" value="Очистить"> </p></form>
Форма вполне стандартна, но тем не менее я опишу ее поля:
ServerName — сюда пользователь будет вводить имя (или IP-адрес) сервера USENET;
UserLogin, UserPass — логин и пароль пользователя на сервере, соответственно.
GroupName — определяет имя группы новостей, из которой будут браться сообщения для отображения и куда будет отправлено сообщение, написанное пользователем;
MessLimit — ниспадающий список из трех пунктов (5/10/15). В нем выбирается количество сообщений, которые должны быть отображены в браузере.
Следующий блок полей определяет заголовки исходящего письма, а также его текст.
В конце две стандартные кнопки Reset и Submit. При нажатии на последнюю данные из формы пересылаются в скрипт, который находится в файле nntpclient.php.
Теперь, когда форма готова, осталось написать сам скрипт. Как уже говорилось, он должен быть в файле с именем nntpclient.php и находиться в той же папке, что и файл с формой. Впрочем, если это не так, то нужно указать соответствующий путь в параметре action тэга form.
Теперь я приведу скрипт с вкрапленными в него комментариями.
<title>Результаты</title> \n";
Теперь при помощи функции split() мы разделяем полученную строку, используя в качестве разделителя пробел. В результате получился массив $GroupInfo следующего содержания:
(211, количестно_новых_сообщений, номер_первого_сообщения, номер_последнего_сообщения, …).
То, что идет дальше, нам не интересно. Для уведомления пользователя о состоянии выбранной им группы выводится сообщение с полученными данными:
$NumMess=$GroupInfo[3]-$GroupInfo[2]+1; if ($NumMess<$MessLimit) { $MessLimit=$NumMess; }
В этом блоке выполняется проверка количества статей в выбранной группе. Если общее количество статей (номер_последней —номер_первой) в данной группе меньше, чем пользователь хочет посмотреть ($MessLimit), то тогда скрипт будет выводить только существующее количество статей.
for ($i=$GroupInfo[3]; $i>=$GroupInfo[3]-$MessLimit; $i--) { fputs($nntpsock, "ARTICLE $i\n"); $answer=fgets($nntpsock, 1000); if (substr($answer, 0,3)==«220»){ while (trim($answer)!=".") { $answer=fgets($nntpsock, 1000); echo $answer."<br>\n"; }} }
Теперь выводим последних $MessLimit статей, которые есть в выбранной группе. Вывод производится с минимальным форматированием. Для того чтобы получить статью, нужно передать серверу команду ARTICLE номер_статьи. Если такая статья есть, тогда сервер ответит:
220 n <a> article retrieved — head and body follow
где:
220 — опять же код, указывающий на то, что команда распознана;
n — номер статьи на сервере;
— message-ID: уникальный идентификатор статьи.
Стоит сказать пару слов о номере статьи и ее идентификаторе. Номер статьи может меняться со временем (при получении сервером новых сообщений, например). Идентификатор же не изменится никогда, но может случиться иное: сервер присвоит такой же идентификатор другому сообщению. Но это случается крайне редко — раз в несколько лет, а к этому времени первая статья, согласитесь, устареет.
После передачи сервером вышеуказанной строки он передает сам текст сообщения: сначала все имеющиеся заголовки, а потом текст статьи. При этом заголовки отделяются от текста одной пустой строкой. Признак конца сообщения выглядит так: \r\n.\r\n (обратите внимание на точку между \r\n). Здесь использован один из двух возможных форматов команды ARTICLE — по номеру статьи. Есть также второй вариант —ARTICLE . Такой формат применяется, если нужно получить статью по ее идентификатору.
Кроме ARTICLE есть еще команды HEADER и BODY для получения только заголовков статьи или же только самого текста. Эти две команды, как и ARTICLE, можно использовать как с номером статьи, так и с ее идентификатором.
На этом процесс получения писем заканчивается, и мы приступаем к отправке сообщения, введенного пользователем. Если пользователь ничего не набрал, значит и отправлять нечего — выходим из скрипта:
if ($PostedMess=="") exit();
Декоративная горизонтальная линия, которая отделит отображенные статьи от последующего вывода:
echo "<hr>";
А теперь посылаем серверу команду POST:
fputs($nntpsock, "POST\n"); $answer=fgets($nntpsock, 100); if (substr($answer,0,3)!="340") { echo "Не удалось отправить письмо на сервер"; exit(); }
Если все в порядке, сервер вернет строку
340 send article to be posted. End with .
где:
340 — код, означающий, что все в порядке. Далее сервер сообщает, что нам можно отправить статью, которую нужно закончить уже упоминавшейся последовательностью \r\n.\r\n.
Если постить в группу запрещено, тогда ответ будет выглядеть так: 440 posting not allowed — извините, не повезло.
$ConvertedMess="From: $FromName Newsgroups: $GroupName Subject: $Subject $PostedMess\r\n.\r\n";
Формируем готовое к отправке сообщение (со всеми заголовками и окончанием) в переменной $ConvertedMess. Добавляем заголовки, введенные пользователем из формы. Как я уже говорил, первая пустая строка в сообщении означает, что все написанное дальше — текст сообщения. Иногда можно попасться на этом, написав вот так:
$ConvertedMess=" From: $FromName
И вновь напоминаю: не забудьте заканчивать сообщение последовательностью \r\n.\r\n. Если сервер ее не найдет, то сообщение будет забраковано и не принято.
fputs($nntpsock, $ConvertedMess);
Теперь передаем сообщение.
echo "Было получено $MessLimit писем.<p>Ваше сообщение было успешно доставлено";
Выводим небольшое сообщение о том, что все «пучком».
?>
Конец скрипта.
Ну вот, осталось только все это набрать, запустить и радоваться. Казалось бы, можно процитировать песню: «Ти просто молодчина, я просто молодець, на цьому все — казочці… кінець». Но тут донеслись крики: «Как это все? А практические рекомендации, советы, выезд на дом с установкой предлагаемого программного обеспечения!?»
Ладно, уговорили.
Первая проблема состоит в том, что некрасиво заваливать группы новостей всякими сообщениями типа «Test». Да и дорого это, тестировать скрипт в онлайне. Вывод напрашивается сам собой: нужно обзавестись NNTP-сервером. Не обязательно, чтобы он был суперкрутым, ведь мы хотим не администрировать новостной сервер, а всего лишь проверить скрипт. Мне в решении этой проблемы помог Eserv (, 1.60 Мб). Программа сочетает в себе функции WWW-, FTP-, POP3-, SMTP-, PROXY-, FINGER- и NNTP-серверов. Так сказать, «все в одном флаконе». Иногда софтина глючит, но в целом весьма функциональна.
Теперь посмотрим, чему мы научились. Во-первых, разобрали механизм использования сокетов в PHP на полезном примере. Таким образом, можно подсоединяться не только к 119 порту, но и к любому другому (за которым следит определенная программа). Частично разобрались с протоколом сетевых новостей. Я описал далеко не все команды протокола NNTP. Для того чтобы получить его полное описание, достаточно лишь зайти на страницу с описанием RFC977 (Request For Comment 977), URL которой указан выше. При желании можно написать крутой USENET-гейт.
Открою секрет: подобно NNTP-протоколу работают и POP, и SMTP (которые применяются для обмена почтой), и все остальные протоколы. Теперь перестает быть секретом, как работает web-интерфейс для почты. Даже HTTP, которым пользуются все (но не все знают об этом), тоже работает по аналогичному принципу.
Для того чтобы понять, как работают все эти протоколы, обратитесь к соответствующим описаниям RFC на сайте http://www.w3.org. Притом что наш скрипт работает на PHP, это вовсе не означает, что сей язык — единственный, поддерживающий сокеты. Их поддержка реализована, например, в том же Delphi, да и во многих других языках программирования, не применяющихся в web-программировании.
P.S. Проверить работу скрипта можно по адресу , а скачать его текст здесь: (ЦЕЛЫХ два килобайта).
