Сегодня речь вновь пойдет о замечательном языке серверных приложений — о 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&lt;$MessLimit) { $MessLimit=$NumMess; }

В этом блоке выполняется проверка количества статей в выбранной группе. Если общее количество статей (номер_последней —номер_первой) в данной группе меньше, чем пользователь хочет посмотреть ($MessLimit), то тогда скрипт будет выводить только существующее количество статей.

for ($i=$GroupInfo[3]; $i&gt;=$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>Ваше сообщение было успешно доставлено";

Выводим небольшое сообщение о том, что все «пучком».

?&gt;

Конец скрипта.

Ну вот, осталось только все это набрать, запустить и радоваться. Казалось бы, можно процитировать песню: «Ти просто молодчина, я просто молодець, на цьому все — казочці… кінець». Но тут донеслись крики: «Как это все? А практические рекомендации, советы, выезд на дом с установкой предлагаемого программного обеспечения!?»

Ладно, уговорили.

Первая проблема состоит в том, что некрасиво заваливать группы новостей всякими сообщениями типа «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. Проверить работу скрипта можно по адресу , а скачать его текст здесь: (ЦЕЛЫХ два килобайта).