ЧАСТЬ 7. Продолжаем практиковаться в программировании на языке Perl. В этой части мы рассмотрим вопросы сетевого программирования, а также узнаем о некоторых полезных модулях.
Сетевые приложения с Net::XXX. Модули класса Net предоставляют возможность работать с различными протоколами прикладного уровня. Это и FTP, и почтовые POP3 и IMAP, и многие другие. Есть также такие возможности, как работа с через proxy. Краткий рассказ о работе с этими модулями и другую документацию можно взять на сайте http://www.pobox.com/~gbarr/libnet/. Вот небольшой обзор семейства Net:
Net::FTP - Работа с FTP Net::IMAP::Simple - Проверить почту с IMAP-сервера Net::IRC - Написать клиента для IRC Net::NNTP - Работа с News протоколом Net::Ping - Ping, как он есть ) Net::POP3 - Почта POP3 Net::Printer - Печать на принтер Net::SMTP Отправка писем через SMTP Net::SMTP::Server - Создание SMTP-сервера Net::SOCKS - Работа с SOCKS-сервером Net::SSH - Работа с секьюрным шеллом Net::Telnet - Телнет Net::Whois - Поиск доменов через InterNIC Net::ICQ2000 - Написать свою аську на Perl ;-))
Все семейство этих модулей называется libnet (также как семейство модулей LWP называется libwww-perl), и вы можете использовать это название при поиске информации в поисковых системах. Большинство модулей работают одинаково как под *nix-системы, так и под всеми любимой и ненавидимой Windows.
Проверим почту с сервера www.pisem.net
use Net::IMAP::Simple; $user="username@pisem.net"; $pass="Мою кошку зовут JHf^!hdg+/" $server = new Net::IMAP::Simple-('smtp.hotbox.ru'); # Подключаемся $server->login($user,$pass); # Логинимся $mess_num = $server->select('INBOX'); # Выбираем папку INBOX foreach $msg ( 1..$mess_num ) { $lines = $server->get($msg); # Получаем письмо в виде ссылки на массив строк print @{$lines}; # Распечатываем на экран } $server->quit(); # Отключаемся
Примерно таким же образом происходит работа с другими модулями семейства Net
Возможно, вам может понадобиться работать с сетевыми пакетами на низком уровне, например собирать/разбирать UDP-пакеты. Здесь вам понадобятся модули семейства NetPacket. Для каждого протокола нужно подключать модуль NetPacket::ИмяПрото-кола. Например NetPacket::UDP, NetPacket: :Ethernet, NetPacket::TCP и и.п.
# Простейший POP3-сниффер из документации NetPacket::TCP use Net::PcapUtils; # Модуль для отлова пакетов use NetPacket::Ethernet qw(:strip); use NetPacket::IP qw(:strip); use NetPacket::TCP; sub process_pkt { my($arg, $hdr, $pkt) = @_; my $tcp_obj = NetPacket::TCP->decode(ip_strip(eth_strip($pkt))); if (($tcp_obj->{src_port} == 110) or ($tcp_obj->{dest_port} == 110)) { print($tcp_obj->{data}); } } # Прослушиваем канал на предмет TCP-пакетов. # Для "пойманного" пакета выполняется proccess_pkt Net::PcapUtils::loop(\&process_pkt, FILTER => 'tcp');
Как произвести сжатие данных? Вам поможет модуль Compress::Zlib. Он позволяет не только работать с файлами, но и архивировать/разархивировать данные "на лету".
use Compress::Zlib; $compressed = compress($source); $source = uncompress($compressed);
Подробнее обо всех возможностях модуля - в perldoc Compress::Zlib.
Шифрование данных. Вы наверняка интересовались, как шифровать и расшифровывать данные с помощью Perl. Здесь пригодится модуль Crypt::DES или его собратья (Crypt::Blowfish и т.п.).
use Crypt::DES; $key = pack("H16", "0123456789ABCDEF"); $crypt = new Crypt::DES $key; $crypted = $cipher->encrypt($mytext); $mytext = $crypt->decrypt($crypted);
Работа с файлами. Если вы не хотите изобретать механизмы различных манипуляций с файлами, то воспользуйтесь модулями семейства File
File::Compare - Сравнение файлов File::Find - Поиск файлов File::Path - Создание/Удаление дерева директорий File::Spec - Манипуляции именами файлов/директорий File::stat - Интерфейс к стандартной функции stat() File::Temp - Работа с временными файлами/директориями use File::Path; mkpath($array_of_dirnames_reference, 0, 0755); rmtree($array_of_dirnames_reference, 1);
Здесь первый парамер - ссылка на массив путей новых директорий. Второй - флажок true/false - сообщать о каждой операции или все делать "молча". Третий параметр у mkpath - права.
Форматы текста. Переводы текста между различными форматами (TXT, POD, RTF, HTML, etc) - дело непростое, и лучше всего доверить его соответствующим модулям. Рассмотрим для начала семейство HTML.
Объекты некоторых из модулей этого семейства интегрируются с LWP-обьектами. Чуть ниже вы это увидите.
POD-формат - формат для документации. В комплекте Perl есть утилиты pod2text, pod2html и т.п. для преобразования в более читабельный вид ;-). Кроме того, имеется семейство модулей POD::***, позволяющее работать с этим форматом и преобразовывать его в нормальное для чтения представление.
Для работы с RTF-текстами имеются модули RTF::Document, RTF::HTML::Converter, и т.п.. Правда, эти модули очень плохо относятся к русским буквам и другим чуждым "буржуинским" программерам символам. Но это не беда. Откройте файл модуля RFT::HTML::Converter (и RFT::Text::Converter тоже) и в строке номер 77 сделайте следующую поправку:
"&#$_;" на сhr($_). Иначе все символы, не являющиеся английскими буквами, превратятся в &#[КодASCII].
Очень силен в обращении с текстовыми данными модуль Text (точнее, его семейство). Это даже не модуль, а целый текстовый процессор, не хуже Word.
use Text::Autoformat; $formatted = autoformat $rawtext, { left=>8, right=>70, justify => 'centre', case => 'lower' };
И это самое малое, что можно сделать с текстом с помощью Text::***. Конвертирование форматов разных ОС, фильтрация, макросы, текстовая графика и многое другое. Что называется, "MustHave".
С кодировками Unicode и всеми проблемами, с ней связанными, вам помогут справиться модули Unicode::***
Модуль Unicode::String позволяет работать со строками этой кодировки, не перекодируя их вручную.
use Unicode::String qw(utf8 utf16 hex); $u = utf8("Unicode uniform string"); $u16 = $u->utf16; # Перевод в utf16 $hex = $u->hex; # в Hex-строку $ucopy = $u->copy; $u->chop; $u->length; $u->index($u2); $u->substr($offset, $length);
Работать с форматом PDF (создавать, читать, писать) вам поможет модуль PDF со всеми своими подмодулями, а также семейство Text::PDF. Они предоставляют вам мощный интерфейс для всевозможных манипуляций с PDF-данными. Вы можете, например, написать скрипт, который будет выдавать красивый вариант страниц вашего сайта для печати. Правда, посетителям с подключением Dial-Up это будет не очень удобно =).
LWP Cooking. Снова я возвращаюсь к вопросам работы с LWP, как и обещал, чтобы рассказать о модулях этого семейства подробнее. Несколько позже мы рассмотрим и модули более низкого уровня - Socket и IO::Socket. Самый простой способ использования LWP:
Если ваша консоль поддерживает перенаправление (символы <>), то индексная страница сайта будет записана в index.html.
Теперь напишем что-нибудь посложнее. Создадим скрипт для скачивания архивов Perl-модулей с сайта ActiveState (www.activestate.com/PPMPackages/zips/6xx-builds-only/). Для начала определимся, какими возможностями должна обладать программа.
1) Скачивание списка модулей.
2) Вывод списка в файл.
2) Скачивание модулей выборочно по списку
3) Возможность соединения через прокси-сервер.
4) Сохранение архивов в заданной директории.
Теперь можно создать конфигурационный файл.
# pmdown.cfg # Адрес страницы списка модулей baseurl=http://www.activestate.com/PPMPackages/zips/6xx-builds-only # Если прокси не используется, оставить эти поля пустыми proxy_addr=192.168.0.1 proxy_port=3128 # Если не используется авторизация на прокси-сервере, эти поля оставить пустыми proxy_user=cmapuk proxy_pass=superpassword # Директория для сохранения savedir=/home/users/cmapuk/modules
Наша программа будет принимать 2 параметра в командной строке:
Ключ, определяющий действие, и имя файла, в следующих комбинациях:
cmd> pmdown -l list.txt скачать список модулей и записать в файл list.txt; cmd> pmdown -d list2.txt открыть файл list2.txt и скачать все модули из списка.
При запуске без параметров будем выдавать help.
#!/path/to/perl if(@ARGV<2){ # Если аргументов меньше двух - пишем хелп и завершаемся print < [Usage]: pmdown Options: -l - Download Module list and save to -d - Download Modules by names from HELP exit; } use LWP::UserAgent; # Подключаем модуль клиента LWP open(CFG,"pmdown.cfg")|| die "$!"; while($line=){ # Читаем конфиг построчно chomp($line); next if $line=~/^#/; # Пропускаем строки-комментарии ($var,$value)=split/=/,$line; # Разделяем строку на параметр/значение $CFG{$var}=$value; # Создаем хэш конфигурации } close(CFG); # Создаем объекты агента и запроса $agent = LWP::UserAgent->new(); $request = HTTP::Request->new(); # Прописываем прокси в виде addr:port, если определено значение $CFG{proxy_addr} $agent->proxy('http',"$CFG{proxy_addr}:-$CFG{proxy_port}") if $CFG{proxy_addr}; # Прописываем параметры авторизации, если определено $CFG{proxy_user} $request->proxy_authorization_basic(-$CFG{proxy_user},$CFG{proxy_user}) if $CFG{proxy_user}; if($ARGV[0] eq "-l"){ $request->uri($CFG{baseurl}); # Отправляем запрос. Ответ в $result $result=$agent->request($request); if($result->is_error()){ print "Error:".$result->message; exit; # Если произошла ошибка, выводим текст и завершаемся } # Все ОК. Создаем из страницы список my $list=$result->content(); my @zips=(); while($list=~/href="\/([^\/]+)\.zip"/){ push(@zips,$1); # В @zips список вида ModuleName } open(F,">$ARGV[1]")|| die "File-$ARGV[1]:$!"; print F join("\n",@zips); close(F); print "\n\n Ok! Check $ARGV[1]"; # Записали в файл и завершаемся exit; }elsif($ARGV[0] eq "-d"){ open(F,"$ARGV[1]")|| die "File-$ARGV[1]:$!"; chomp(@zips=); # Читаем список модулей close(F); for(@zips){ $request->uri("$CFG{baseurl}/$_.zip"); # Составляем адрес $result=$agent->request($request, "$CFG{savedir}/$_.zip"); # запрашиваем if($result->is_success()){ # Если все ок, файл сохранится print "Module $_: saved.\n"; # А мы выводим сообщение $ok++; }else{ print "Module $_: error\n".$result->message()."\n"; $failed++; # Если произошла ошибка - пишем сообщение } } print "$ok modules saved\n$failed modules no saved"; exit; # Все. Подсчитали модули OK/NotOK и завершаемся }else{ # Неправильные аргументы. Завершаемся print "Type: \"cmd>pmdown h\" for help"; exit; }
Пример кода не блещет красотой исполнения, но зато так выглядит нагляднее. В качестве "домашнего задания" могу предложить оптимизировать код примера или расширить его возможности. Если ваш новый скрипт будет работать, значит, я не зря потратил время, взявшись за написание этой статьи ;-).
По материалам журнала Компьютер Price (www.comprice.ru)