ЭЛЕКТРОННАЯ БИБЛИОТЕКА КОАПП |
Сборники Художественной, Технической, Справочной, Английской, Нормативной, Исторической, и др. литературы. |
Протоколы ИнтернетаВведениеПравильная работа с сокетами - лишь часть программирования сетевых коммуникаций. Даже если вы организовали обмен данными между двумя программами, все равно вам понадобится определенный протокол. С помощью протокола каждая сторона узнает, когда передаются или принимаются данные и кто именно отвечает за данный аспект службы. Наиболее распространены следующие протоколы Интернета.Протокол- Расшифровка FTP -File Transfer Protocol telnet rsh и rep -Remote shell and Remote Copy NNTP -Network News Transfer Protocol HTTP -Hypertext Transfer Protocol SMTP -Simple Mail Transfer Problem РОРЗ - Post Office Protocol Описание Копирование файлов между удаленными компьютерами Удаленное подключение к компьютеру Удаленная регистрация и копирование файлов Чтение п отправка новостей Usenet Пересылка документов по Web Отправка почты Чтение почты Даже такая относительно простая задача, как подключение к удаленному компьютеру, требует довольно сложных переговоров между клиентом и сервером и многочисленных параметров с динамической настройкой. Если бы при каждой попытке воспользоваться сетевой службой вам приходилось писать код Perl с реализацией этих протоколов, ничего хорошего бы не вышло - программы содержали бы неимоверное количество ошибок. К счастью, на CPAN имеются модули для всех протоколов. Большинство модулей реализует клиентскую, а не серверную сторону протокола. Следовательно, программа сможет использовать эти модули для отправки почты, но не для выполнения функций почтового сервера, к которому подключаются другие клиенты. Она может читать и отправлять новости, но не являться сервером новостей для других клиентов; обмениваться файлами с сервером FTP, но не быть сервером FTP; и т. д. Большинство этих модулей принадлежит иерархии Net::. Модуль Net::FTP используется для отправки и приема файлов по FTP; модуль Net::NNTP - для чтения и отправки новостей Usenet; модуль Net::Telnet - для имитации подключения к другому компьютеру; модуль Net::Whois - для получения данных об имени домена; модуль Net::Ping - для проверки связи с компьютером, а модули Net::POP3 и Mail::Mailer - для отправки и получения почты. Протокол CGI рассматривается в главе 19 "Программирование CGI", а протокол HTTP - в главе 20 "Автоматизация в Web". Большинство этих модулей написал Грэхем Барр, автор модулей IO::Socket, использовавшихся в низкоуровневых сетевых коммуникациях в главе 17 "Соке-ты". Он написал Net::FTP, Net::NNTP, Net::POP3 и Mail::Mailer. Джей Роджерс (Jey Rogers) написал Net::Telnet, а Чип Зальцепберг (Chip Salrenberg) - Net::Whois. Благодаря им вам не придется заново изобретать велосипед! 18.1. Простой поиск в DNSПроблемаТребуется определить IP-адрес хоста или преобразовать IP-адрес в имя. Сетевые серверы решают эту задачу в процессе аутентификации своих клиентов, а клиенты - когда пользователь вводит имя хоста, но для библиотеки сокетов Perl нужен IP-адрес. Более того, многие серверы регистрируют в файлах журналов IP-адреса, но аналитическим программам и людям удобнее работать с именами хостов.РешениеДля получения всех IP-адресов по имени хоста (например, www.perl.com) воспользуйтесь функцией get host byname:use Socket; @addresses = gethostbyname($name) or die "Can't resolve $name: $!\n"; @addresses = map { inet_ntoa($_) } @addresses[4 .. $"addresses], # @addresses - список IP-адресов ("208.201.239.48", "208.201.239.48") Если вам нужен только первый адрес, воспользуйтесь функцией inet_aton: use Socket; $address = inet_ntoa(inet_aton($name)); # $address - один IP-адрес ("208.201.239.48") Для получения имени хоста по строке с IP-адресом (например, "208. 201. 239.48"), воспользуйтесь следующим фрагментом: use Socket; $name = gethostbyaddr(inet_aton($address), AF_INET) or die "Can't resolve $address: $!\n"; # $name - имя хоста ("www.perl.com") КомментарийНаша задача усложняется тем, что функции Perl являются простыми оболочками для системных функций С, поэтому IP-адреса приходится преобразовывать из ASCII-строк ("208. 201. 239. 48") в структуры С. Стандартный модуль Socket содержит функцию inet_aton для перехода от ASCII к упакованному числовому формату и функцию inet_ntoa, выполняющую обратное преобразование:use Socket; $packed_address = inet_aton("208.146.140.1"); $ascii_address = inet_ntoa($packed_address); Функция gethostbyname получает строку, содержащую имя хоста (или IP-адрес). В скалярном контексте она возвращает IP-адрес удаленного хоста, который можно передать inet_ntoa (или undef в случае ошибки). В списковом контексте она возвращает список, состоящий по крайней мере из пяти элементов (или пустой список в случае ошибки). Список состоит из следующих элементов. Индекс Значение О Официальное имя хоста 1 Синонимы (строка, разделенная пробелами) 2 Тип адреса (обычно AF_INET) 3 Длина структуры адреса (не имеет значения) 4,5... Структуры адресов Имени хоста может соответствовать несколько IP-адресов; в частности, это происходит на сильно загруженных Web-серверах, где для снижения загрузки на разных компьютерах размещаются идентичные Web-страницы. В подобных ситуациях сервер DNS, предоставляющий адреса, чередует их, обеспечивая сбалансированную нагрузку на сервер. Если вы хотите выбрать IP-адрес для подключения, просто возьмите первый адрес в списке (а если он не работает, попробуйте остальные адреса): $packed = gethostbyname($hostname) or die "Couldn't resolve address for $hostname: $!\n"; $address = inet_ntoa($packed); print "I will use $address as the address for $hostname\n"; Используя имена хостов для разрешения или отказа в обслуживании, будьте осторожны. Любой желающий может настроить свои сервер DNS так, чтобы его компьютер идентифицировался как www.whitehouse.gov, www.yahoo.corn или this.is.not. funny. Нельзя сказать, действительно ли ему принадлежит то имя, на которое он претендует, пока вы не вызовете gethostbyname и не проверите исходный адрес по адресному списку для данного имени. # $address - проверяемый IP-адрес (например, "128.138.243.20") use Socket; $name = gethostbyaddr(inet_aton($address), AF_INET) or die "Can't look up $address : $!\n"; @addr = gethostbyname($name) or die "Can't look up $name : $!\n"; $found = grep { $address eq inet_ntoa($_) } @addr[4. .$#addr]; Оказывается, даже такой алгоритм не дает полной уверенности в полученном имени, поскольку существуют разнообразные обходные пути. Даже IP-адрес, из которого вроде бы поступают пакеты, может быть поддельным, и в процессе аутентификации не следует полагаться на сетевой уровень. В действительно важных ситуациях всегда выполняйте аутентификацию сами (с помощью паролей или криптографических методов), поскольку сеть IPv4 не проектировалась для соблюдения безопасности. Информация о хосте не ограничивается адресами и синонимами. Чтобы полноценно работать с дополнительными данными, воспользуйтесь модулем Net::DNS с СРАМ. Программа 18.1 показывает, как получить записи MX (mail exchange) для произвольного хоста. Пример 18.1. mxhost #!/usr/bin/perl # mxhost - поиск записей mx для хоста use Net::DNS; $host = shift; $res = Net::DNS::Resolver->new(); @mx = mx($res, $host) or die "Can't find MX records for $host (".$res->errorstring,")\n"; foreach $record (@mx) { print $record->preference, " ", $record->exchange, "\n"; } Примерный вывод выглядит так: % mxhost cnn.corn 10 niail.turner.coin 30 alfw2.turner.com Функция inet_aton, как и gethostbyname, получает строку с именем хоста или IP-адресом, однако она возвращает только первый IP-адрес для данного хоста. Чтобы узнать все IP-адреса, приходится писать дополнительный код. Модуль Net::hostent поддерживает соответствующие средства доступа по имени. Пример 18.2 показывает, как это делается. #!/usr/bin/perl # hostaddrs - канонизация имени и вывод адресов use Socket; use Net::hostent; $name = shift; if ($hent = gethostbyname($name)) { $name = $hent->name; # Если отличается $addr_ref = $hent->addr_list; @addresses = map { inet_ntoa($_) } @$addr_ref; } print "$name => @iaddresses\n"; Примерный результат выглядит так: % hostaddrs www.ora.com helio.ora.com => 204.148.40.9 % hostaddrs www.wriitc'iouse. gov www.whitehouse.gov => 198.137.240.91 198.137.240.92 [> Смотри также --------------------------------
18.2. Клиентские операции FTPПроблемаВы хотите подключиться к серверу FTP, чтобы отправить или принять с пего файлы. Например, вы решили автоматизировать разовую пересылку многих файлов или автоматически создать зеркальную копию целого раздела сервера FTP.РешениеВоспользуйтесь модулем Net::FTP с CPAN.use Net::FTP; $ftp = Net::FTP->new("ftp.host.com") or die "Can't connect: $@\n"; $ftp->login($username, $password) or die "Couldn't login\n"; $ftp->cwd($directory) or die "Couldn't change directory\n"; $ftp->get($filename) or die "Couldn't get $filename\n"; $ftp->put($filename) or die "Couldn't put $filename\n"; КомментарийРабота с модулем Net::FTP состоит из трех шагов: подключение к серверу, идентификация и аутентификация и пересылка файлов. Все операции с сервером FTP реализуются методами объекта Net::FTP. При возникновении ошибки методы возвращают undef в скалярном контексте и пустой список в списковом контексте. Подключение осуществляется конструктором new. В случае ошибки переменной $@ присваивается сообщение об ошибке, a new возвращает undef. Первый аргумент определяет имя хоста сервера FTP и может сопровождаться необязательными параметрами:$ftp = Net::FTP->new("ftp.host.corn", Timeout => 30, Debug => 1) or die "Can't connect: $@\n"; Параметр Timeout определяет промежуток времени в секундах, после которого любая операция считается неудачной. Параметр Debug устанавливает уровень отладки (при значении, отличном от нуля, копии всех команд отправляются в STDERR). Строковый параметр Firewall определяет компьютер, являющийся прокси-серве-ром FTP. Параметр Port задает альтернативный номер порта (по умолчанию используется значение 21, стандартный номер порта FTP). Наконец, если параметр Passive равен true, все пересылки выполняются в пассивном режиме (требование некоторых брандмауэров и прокси-серверов). Параметры Firewall и Passive переопределяют переменные окружения FTP_FIREWALL и FTP_PASSIVE. Следующим после подключения шагом является аутентификация. Обычно функция login вызывается с тремя аргументами: именем пользователя, паролем и учетной записью (account). $ftp->login() or die "Couldn't authenticate.\n"; $ftp->login($username) or die "Still couldn't authenticate.\n"; $ftp->login($username, $password) or die "Couldn't authenticate, even with explicit username and password.\n"; $ftp->login($username, $password, $account) or die "No dice. It hates me.\n"; Если вызвать login без аргументов, Net::FTP с помощью модуля Net::Netrc определяет параметры хоста, к которому вы подключились. Если данные не найдены, делается попытка анонимной регистрации (пользователь anonymous, пароль username@hostname). Если при имени пользователя anonymous пароль не задан, в качестве пароля передается почтовый адрес пользователя. Дополнительный аргумент (учетная запись) в большинстве систем не используется. При неудачной аутентификации функция login возвращает undef. После завершения аутентификации стандартные команды FTP выполняются с помощью методов, вызываемых для объекта Net::FTP. Методы get и put принимают и отправляют файлы. Отправка файла выполняется так: $ftp->put($localfile, $remotefile) or die "Can't send $localfile: $!\n' Если второй аргумент отсутствует,
имя удаленного файла совпадает с именем локального файла. Передаваемые
данные также можно брать из файлового манипулятора (в этом случае имя удаленного
файла передается в качестве второго аргумента):
> Смотри также -------------------------------
18.3. Отправка почтыПроблемаВаша программа должна отправлять почту. Некоторые программы следят за системными ресурсами - например, свободным местом на диске - и рассылают сообщения о том, что ресурс достиг опасного предела. Авторы сценариев CGI часто делают так, чтобы при нарушениях работы базы данных программа не сообщала об ошибке пользователю, а отправляла сообщение о проблеме администратору базы данных.РешениеВоспользуйтесь модулем Mail::Mailer с CPAN:use Mail::Mailer; Smaller = Mail::Mailer->new(); $mailer->open({ From => $from_address, To => $to_address, Subtect => $subject, }) or die "Can't open: $!\n"; print $mailer $body; $mailer->close(); Кроме того, можно воспользоваться программой sendmail: open(SENDMAIL, "1/usr/lib/sendmail -oi -t -odq") or die "Can't fork for sendmail: $!\n"; print SENDMAIL ""EOF"; From: User Originating Mail A relevant subject line Body of the message goes here, in as many lines as you like. EOF close(SENDMAIL) or warn "sendmail didn't close nicely"; КомментарийСуществуют три варианта отправки почты из программы. Во-первых, можно воспользоваться внешней программой, которая обычно применяется пользователями для отправки почты - например, Mail или mailx; такие программы называкл -ся "пользовательскими почтовыми агентами" (MUA, Mail User Agents). Во-вторых, существуют почтовые программы системного уровня (например, sendmail); они называются "транспортными почтовыми агентами" (МТА, Mail Transport Agents). Наконец, можно подключиться к серверу SMTP (Simple Mail Transfer Protocol). К сожалению, стандартной программы пользовательского уровня не существует, для sendmail не определено стандартного местонахождения, а протокол SMTP довольно сложен. Модуль Mail::Mailer от CPAN избавляет вас от этих сложностей.При установке модуль Mail::Mailer ищет mail, Mail и другие имена, под которыми обычно скрываются программы отправки почты. Кроме того, он просматривает некоторые распространенные каталоги, где может находиться sendmail. При создании объекта Mail::Mailer вы получаете удобный доступ к этим программам (и почтовым серверам SMTP), не заботясь о структуре аргументов или о возвращаемых ошибках. Создайте объект Mail::Mailer конструктором Mail: :Maller->new. При вызове конструктора без аргументов используется метод отправки почты по умолчанию (вероятно, с помощью внешней программы типа mail). Аргументы new позволяют выбрать альтернативный способ отправки сообщений. Первый аргумент определяет способ отправки ("mail" для пользовательских почтовых агентов UNIX, "sendmail" для программы sendmail и "smtp" для подключения к серверу SMTP). Необязательный второй аргумент определяет путь к программе. Например, следующая команда приказывает Mail::Mailer использовать sendmail вместо способа отправки, принятого но умолчанию: $maller = Mail::Mailer->new("sendmail"); В следующем примере вместо mail используется почтовая программа /u/gnat/ bin/funkymailer: Smaller = Mail::Mailer->new("mail", "/u/gnat/bin/funkymailer"); Подключение к серверу SMTP mail.myisp.com выполняется так: $mailer = Mail::Mailer->new("smtp", "mail.myisp.com"); При возникновении ошибки в любой части Mail::Mailer вызывается die. Следовательно, для проверки ошибок следует включить код отправки почты в блок eval, после чего проверить переменную $@: eval { $maller = Mail::Mailer->new("bogus", "arguments"); # ... }; if ($@) { # Неудачный вызов eval print "Couldn't send mail: $@\n"; } else { # Успешный вызов eval print "The authorities have been notified,\n": } Если конструктор new не понимает переданные аргументы или не имеет метода по умолчанию при отсутствии аргументов, он инициирует исключение. Модуль Mail::Mailer запускает почтовую программу или подключается к серверу SMTP лишь после вызова метода open для заголовков сообщения: $mailer->open( 'From' => 'Nathan Torkington 'To' => 'Tom Christiansen 'Subject' => 'The Perl Cookbook' ); Если попытка запустить программу или подключиться к серверу завершилась неудачно, метод open инициирует исключение. После успешного вызова open переменную Smaller можно интерпретировать как файловый манипулятор и вывести в нее основной текст сообщения: print Smaller "EO_SIG; Мы когда-нибудь закончим эту книгу? Жена грозится уйти от меня. Она говорит, что я люблю EMACS больше, чем ее, Что делать? Нат EO_SIG Завершив отправку текста, вызовите функцию close для объекта Mail::Mailer: close($mailer) or die "can't close mailer: $!"; Впрочем, с программой sendmail можно общаться и напрямую: open(SENDMAIL, "1/usr/sbin/sendmail -oi -t -odq") or die "Can't fork for sendmail: $!\n"; print SENDMAIL ""EOF"; From: Tom Christiansen Subject: Re: The Perl Cookbook (1) Мы никогда не закончим эту книгу. (2) Тот, кто работает с EMACS, не заслуживает любви. (3) Переходи на vi. Том EOF close(SENDMAIL); Перед нами тривиальное использование функции open для запуска другой программы (см. рецепт 16.4). Нам приходится указывать полный путь к sendmail, поскольку местонахождение этой программы меняется от компьютера к компьютеру. Обычно она находится в каталоге /usr/lib или /usr/sbm. Флаги, передаваемые sendmail, говорят о том, что программа не должна завершаться при чтении строки, состоящей из одной точки (-ог); что адресат сообщения определяется по данным заголовка (-?); а также о том, что вместо немедленной доставки сообщение должно помещаться в очередь (-odq). Последний параметр важен лишь при отправке больших объемов почты - без него компьютер быстро захлебнется в многочисленных процессах sendmail. Чтобы сообщение доставлялось немедленно (например, во время тестирования или при срочной доставке почты), удалите -odq из командной строки. Мы выводим функцией print все сообщение - заголовки и основной текст, разделяя их пустой строкой. Не существует специальных служебных символов для вставки новых строк (как в некоторых пользовательских почтовых программах), поэтому весь текст интерпретируется буквально. Sendmail ^сбавляет заголовки Date и Message-ID (которые все равно пришлось бы генерировать вручную). Некоторые версии Perl (особенно для Windows и Мае) не имеют аналогов sendmail или mail. В таких случаях отправка почты осуществляется через сервер SMTP. > Смотри также
18.4. Чтение и отправка новостей UsenetПроблемаВы хотите подключиться к серверу новостей Usenet для чтения и отправки новостей. Ваша программа может периодически отправлять материалы, собирать статистику по конференции или идентифицировать новичков, чтобы отправить им приветственное сообщение.РешениеВоспользуйтесь модулем Net::NNTP с СРАМ:use Net::NNTP; $server = Net::NNTP->new("news.host.dom") or die "Can't connect to news server: $@\n"; ($narticles, $first, $last, $name) = $server->group( "misc.test" ) or die "Can't select misc.test\n"; $headers = $server->head($first) or die "Can't get headers from article $first in $name\n"; $bodytext = $server->body($first) or die "Can't get body from article $first in $name\n"; $article = $server->article($first) or die "Can't get article $first from $name\n"; $server->postok() or warn "Server didn't tell me I could post.\n"; $server->post( [ @lines ] ) or die "Can't post: $!\n"; КомментарийUsenet представляет собой распределенную систему конференций. Обмен сообщениями между серверами обеспечивает одинаковое содержимое всех находящихся на них конференций. Каждый сервер устанавливает собственный критерий, определяющий максимальный срок хранения сообщений. Клиентские программы подключаются к выделенному серверу (обычно принадлежащему их организации, Интернет-провайдеру или университету), получая возможность читать существующие или отправлять новые сообщения.Каждое сообщение состоит из набора заголовков и основного текста, разделенных пустой строкой. Сообщения идентифицируются двумя способами: заголовком идентификатора сообщения и номером сообщения в конференции. Идентификатор сообщения хранится внутри самого сообщения. Он заведомо остается уникальным независимо от того, с какого сервера Usenet было прочитано сообщение. Если сообщение ссылается на другие сообщения, оно также использует их идентификаторы. Идентификатор сообщения представляет собой строку вида: <0401@jpl-devvax.JPL.NASA.GOV> Также возможна идентификация сообщений по конференции и номеру внутри конференции. Каждый сервер Usenet присваивает своим сообщениям собственные номера, поэтому правильность ссылок гарантирована лишь для того сервера Usenet, с которого они были получены. Конструктор Net::NNTP подключается к заданному серверу Usenet. Если соединение не удается установить, он возвращает undef и присваивает переменной $@ сообщение об ошибке. Если соединение было успешно установлено, new воз-цр;|щяпт новый пбъ скт Net::NNTP: $server = Net::NNTP->new("news.mycompany.corn") or die "Couldn't connect to news.mycompany.com: $@\n"; После установки соединения метод list возвращает список конференций в виде ссылки на хэш, ключи которого соответствуют именам конференций. Ассоциированные значения представляют собой ссылки на массивы, содержащие первый допустимый номер сообщения в конференции, последний допустимый номер сообщения в конференции и строку флагов. Флаги обычно равны "у" (отправка разрешена), но также могут быть равны "т" (модерируемая конференция) или =ИМЯ (данная конференция дублирует конференцию ИМЯ). На сервере могут храниться свыше 17 000 конференций, поэтому выборка всего списка требует некоторого времени. $grouplist = $server->list() or die "Couldn't fetch group list\n"; foreach $group (keys %$grouplist) { if ($grouplist->{$group}->[2] eq 'y') { # Отправка в $group разрешена } } По аналогии с концепцией текущего каталога в FTP, протокол NNTP (NetNews Transfer Protocol) поддерживает концепцию текущей конференции. Назначение текущей конференции выполняется методом group: ($narticles, $first, $last, $name) = $server->group("comp.lang.perl.misc") or die "Can't select сотр.lang.perl.misc\n"; Метод group возвращает список из четырех элементов: количество сообщений в конференции, номер первого сообщения, номер последнего сообщения и название конференции. Если конференция не существует, возвращается пустой список. Содержимое сообщений можно получить двумя способами: вызвать метод article с идентификатором сообщения или выбрать конференцию методом group, а затем вызвать article с номером сообщения. В скалярном контексте метод возвращает ссылку на массив строк. В списковом контексте возвращается список строк. При возникновении ошибки article возвращает false: @lines = $server->article($message_id) or die "Can't fetch article $article_number: $!\n"; Для получения заголовка и основного текста сообщения используются соответственно методы head и body. Как и article, они вызываются для идентификатора или номера сообщения и возвращают список строк или ссылку на массив: @group = $server->group("comp.lang.perl.misc") or die "Can't select group сотр.lang.perl,misc\n"; @lines = $server->head($group[1]) or die "Can't get headers from first article in сотр.lang.perl.misc\n"; Метод post отправляет новое сообщение. Он получает список строк или ссылку на массив строк и возвращает true при успешной отправке или false в случае неудачи. $server->post(@triessage) or die "Can't post\n"; Метод postok позволяет узнать, разрешена ли отправка сообщений на данный сервер Usenet: unless ($server->postok()) { warn "You may not post,\n"; } Полный список методов приведен в man-странице модуля Net::NNTP. > Смотри также ------------------------------
|