Платный
хостинг от провайдера HostSpace.com.ua - хостинг, регистрация доменов.
Поддержка PHP, MySQL, почта - в каждом тарифном плане.
Hello, Perl! Perl FAQ по-русски
Дмитрий Репин aka cmapuk[0nline]
ЧАСТЬ 4.
В этой части статьи мы рассмотрим не менее популярные, чем CGI-программирование, аспекты использования Perl - клиент-серверные приложения и программы для работы с базами данных.
Технология витья "гнезд" и LWP-кулинария
"Крылья... Ноги... Главное - хост!
Во!..."
c2002 cmapuk[0nline]
Плетем гнезда. Стандартные инструменты Перл для работы с Сетью достаточно удобны и, в принципе, не нуждаются в надстройках. Исключение может составить разве что только модуль LWP. В этом разделе мы плавно пойдем от use Socket до use LWP; и таким образом попытаемся охватить все возможности сетевого Perl. Итак, стандартный модуль Socket.
Socket - это модуль функционального типа. Описание всех его функций имеется в perldoc. Мы же рассмотрим сами принципы организации сетевых подключений. Не забывайте, что команда perldoc -f function - расскажет много интересного.
use Socket; # Подключаем модуль
$|++; # Отключаем буферизацию
$request=<<REQ; # Составляем запрос в переменную $request
GET /go.cgi?action=forum\&board=dummies HTTP/1.0
User-Agent:LLamzilla Platinum 3000
Referer:http://www.microsux.com
Cookie:Da kakie nafig kuki!
Accept:*/*
Accept-Language:en;ru
Accept-Charset:koi8-r
REQ
$protocol=getprotobyname(tcp);
# Эта функция возвращает числовое обозначение по имени
# протокола. Мы используем tcp протокол.
# На TCP основаны все прикладные протоколы, например
# HTTP, FTP, SMTP, POP3, и т.д.
socket(SOCK,PF_INET,SOCK_S-TREAM,$protocol);
# Открываем сокет с дескриптором SOCK(как файл в open).
# Вторым параметром мы определяем либо сокет юниксов, либо сетевой
# сокет. В данном случае - сетевой.
# Третий параметр - тип передачи данных.
# SOCK_STREAM-потоковый (для tcp), требующий подключения к удаленному
# хосту. SOCK_DGRAM - не требует подключения, используется для передачи
# данных отдельными пакетами(udp). Кстати, по большей части ICQ
# работает как раз на udp.
$iaddr=gethostbyname(perl.ru);
# Получаем адрес по имени
$port=80;
$packed_addr=sockaddr_in-($port,$iaddr);
# Упаковываем данные для подключения в
# специальный формат(для функции connect())
if(connect(SOCK,$packed_addr)){
print SOCK $request;
# Если подключение состоялось, посылаем запрос
# и получаем ответ
while(<SOCK>){
push(@response,$_);
}
}else{
print "Perl.ru в дауне =(";
}
close(SOCK);
# Теперь в массиве @response у нас лежит ответ(примерно так):
# 200 OK Found
# Заголовки
# ...
# ...
# Пустая строка
# HTML-страница http://www.perl.ru/go.cgi?action=forum&board=dummies
Для метода POST вся работа с сокетом аналогична. Изменяется только запрос.
$request=<<REQ;
POST /go.cgi HTTP/1.0
User-Agent:LLamzilla Platinum 3000
Referer:http://www.microsux.com
Cookie:Da kakie nafig kuki!
Accept:*/*
Accept-Language:en;ru
Accept-Charset:koi8-r
Content-type:application/www-form-urlencoded
Content-Length:26
action=forum\&board=dummies
REQ
Конечно, данный способ менее всего удобен, но он позволяет контролировать весь процесс на всех стадиях его действия. Рассмотрим теперь этот же пример, но с использованием модуля IO::Socket;
# Запрос тот же в $request
$sock = IO::Socket::INET->new(PeerAddr => www.perl.ru,
PeerPort => 80,
Proto => tcp
Type => SOCK_STREAM);
# Или так
# $sock = IO::Socket::INET->new(PeerAddr => www.perl.ru:80);
# $sock = IO::Socket::INET->new("XXX.XXX.XXX.XXX:80");
# Параметры метода new() помогают ввести дополнительные опции
# подключения, например Timeout.
if($sock){
print $sock $request;
while(<$sock>){
push(@response,$_);
}
}
close($sock);
# то же самое: if $sock значит, подключение состоялось
# и мы отправляем данные, а потом принимаем
# Для приема и отправки также существуют ф-ции send() и recv()
Не сложнее, чем работа с файлами, правда?
Стоит еще раз отметить: для UDP используется тип сокета SOCK_DGRAM и не требуется connect(), а для TCP - наоборот. В разделе "Чистая практика" мы еще вернемся к Socket и IO::Socket, а сейчас рассмотрим модуль LWP.
LWP=lib-www-perl
Этот модуль весьма популярен среди программистов. Он представляет собой удобный интерфейс к модулям Socket и IO::Socket. Cобственно, лучшего рассказа про LWP, чем perldoc lwpcook, найти сложно. Поэтому я просто возьму примеры оттуда и сделаю комментарии.
Метод GET при использовании LWP-Simple
use LWP::Simple;
$doc = get("http://www.perl.ru");
# Весь HTML в $doc
getprint("http://www.perl.ru");
# Вывод страницы сразу в STDOUT
Запуск с консоли
>perl -MLWP::Simple -e getprint "http://www.sn.no/libwww-perl/";
Распечатка в STDIN
>perl -MLWP::Simple -e getstore "ftp://ftp.sunet.se/pub/lang/perl/CPAN/src/latest.tar.gz";,"perl.tar.gz"
Скачать и сохранить файл как "perl.tar.gz"
######## В скрипте
use LWP::UserAgent;
$ua = LWP::UserAgent->new;
$ua->agent("Gruzilla/10.0 Platinum"); # Типа кул броузер )
$req = HTTP::Request->new(GET => http://www.perl.ru);
# Модуль HTTP::Request сделан для удобства создания запросов
$req->header(Accept => text/html);
# Теперь отправляем
$res = $ua->request($req);
# Проверим, все ли прошло ОК
if ($res->is_success) {
print $res->content;
# Распечатка принятых данных
} else {
print "Error: " . $res->status_line . "\n";
}
Метод HEAD для LWP-Simple
При методе HEAD данные не отправляются и не принимаются.
Между клиентом и сервером идет обмен только заголовками.
Это полезно, например, для проверки существования файлов на сервере и т.п.
use LWP::Simple;
if (@hdrs=head($url)) {
# OK документ существует и доступен
print join("\n",@hdrs);
# Распечатаем заголовки
}
Метод POST (LWP-Simple нет)
use LWP::UserAgent;
$ua = LWP::UserAgent->new;
# Составление запроса
my $req = HTTP::Request->new(POST => http://www.someserver.com/script.cgi);
$req->content_type(application/x-www-form-urlencoded);
$req->content(param1=value1& param2=value2); # Это данные якобы формы
my $res = $ua->request($req); # Отправка
print $res->as_string; # Распечатка результата отправки
Еще вариант
use HTTP::Request::Common qw(POST);
use LWP::UserAgent;
$ua = LWP::UserAgent->new;
my $req = POST http://www.someserver.com/script.cgi,
[ param1 => value1, param2 => value ];
print $ua->request($req)->as_string;
Используем PROXY-сервер
use LWP::UserAgent;
$ua = LWP::UserAgent->new;
$ua->env_proxy; # Взять прокси-настройки из окружения
# или вручную
$ua->proxy(ftp => http://proxy.myorg.com);
$ua->proxy(wais => http://proxy.myorg.com);
my $req = HTTP::Request->new(GET => wais://www.xxx.com/);
print $ua->request($req)->as_string;
Если прокся с паролем
use LWP::UserAgent;
$ua = LWP::UserAgent->new;
$ua->proxy([http, ftp] => http://proxy.myorg.com);
$req = HTTP::Request->new(GET,"http://www.perl.com";);
$req->proxy_authorization_basic("proxy_user", "proxy_password");
$res = $ua->request($req);
print $res->content if $res->is_success;
Доступ к ресурсам, защищенным htaccess
use LWP::UserAgent;
$ua = LWP::UserAgent->new;
$req = HTTP::Request->new(GET => http://www.linpro.no/secret/);
$req->authorization_basic(login, password);
print $ua->request($req)->as_string;
А Куки?
use LWP::UserAgent;
use HTTP::Cookies;
$ua = LWP::UserAgent->new;
$ua->cookie_jar(HTTP::Cookies->new(file => "lwpcookies.txt",
\t\t\t autosave => 1));
# Теперь куки будут читаться/писаться в lwpcookies.txt
# Далее уже запрос
$res = $ua->request(HTTP::Request->new(GET => "http://www.yahoo.no";));
print $res->status_line, "\n";
Секьюрный протокол HTTPS (SSL)
use LWP::UserAgent;
my $ua = LWP::UserAgent->new;
my $req = HTTP::Request->new(GET => https://www.helsinki.fi/);
my $res = $ua->request($req);
if ($res->is_success) {
print $res->as_string;
}else{
print "Failed: ", $res->status_line, "\n";
}
# Код от обычного (для HTTP) мало чем отличается ;-)
Создание зеркальных копий страниц
use LWP::Simple;
%mirrors = (
http://www.perl.ru/ => perl-ru.html,
http://www.perl.com/ => perl-com.html,
http://www.cpan.org =>cpan-org.html,
gopher://gopher.sn.no/ => gopher.html,
);
while (($url, $localfile) = each(%mirrors)) {
mirror($url, $localfile);
}
Доставка больших документов
1-й вариант. Записываем данные в процессе скачивания в файл
use LWP::UserAgent;
$ua = LWP::UserAgent->new;
my $req = HTTP::Request->new(GET => http://www.linpro.no/lwp/libwww-perl-5.46.tar.gz);
$res = $ua->request($req, "libwww-perl.tar.gz");
if ($res->is_success) {
print "ok\n";
}else{
print $res->status_line, "\n";
}
2-й вариант. Вставка контролирующей подпрограммы в качестве второго аргумента $ua->request(). В этом варианте можно записывать скачиваемый файл по кусочкам, чтобы в случае обрыва связи потом его докачать.
use LWP::UserAgent;
$ua = LWP::UserAgent->new;
$URL = ftp://ftp.unit.no/pub/rfc/rfc-index.txt;
my $expected_length;
my $bytes_received = 0;
my $res =$ua->request(HTTP::Request->new(GET => $URL),
sub {
my($chunk, $res) = @_;
$bytes_received += length($chunk);
unless (defined $expected_length) {
$expected_length = $res->content_length || 0;
}
if ($expected_length) {
printf STDERR "%d%% - ",
100 * $bytes_received / $expected_length;
}
print STDERR "$bytes_received bytes received\n";
# В $chunk - текущие данные
# print $chunk;
}
);
print $res->status_line, "\n";
Вот такой простой модуль. К нему мы еще вернемся, а сейчас перейдем к вопросам использования баз данных.
От TXT до SQL
Информация имеет силу, только когда ее можно сохранить.
Перефразированный Карнеги ;-)
Простые задачи. Perl, как и любой полноценный язык, предоставляет возможность сохранять/читать данные в/из различных источников, от файлов до SQL-образных баз данных. При таком многообразии главное - сделать правильный выбор. Для начала стоит определиться, с какими объемами данных будет работать программа, в какой форме эти данные и как часто будет использоваться программа. Для простейших задач подойдет и простой текстовый файл. Например, конфигурация программы.
# config.dat
# Коментарии
# к конфигу
DATADIR=/home/vasya/coolproga/datfiles
EXITCODE=666
MESSAGE=Hello, Master Pupkin!
...
# end of config.dat
В скрипте все это читаем:
open(F,"config.dat");
while($line=<F>){
chomp $line;
next if $line=~/^#/;
# Пропустить строку комментариев
($var,$value)=split/=/,$line;
$CONF{$var}=$value;
# Конфиг собираем в хэш
}
close(F);
Часто более удобным способом хранения информации являются простые базы данных - DBM-файлы. В этих файлах хранятся пары ключ/значение, а работа с данными производится через хэш, ассоциированный с DBM-файлом. DBM-базы бывают различных типов, зависящих обычно от операционной системы. Таблицу совместимости можно найти тут же - perldoc AnyDBM_File . Каждому типу соответствует свой Perl-модуль(DB_File, NDBM_File, etc). Связать хэш с базой можно разными способами - с помощью функций dbmopen или tie.
use DB_File;
dbmopen(%DATA, $dbfile)
or die "Cant open $dbfile: $!";
# Работаем с данными
...
dbmclose(%DATA); # Завершаем работу с базой
use NDBM_File;
tie(%DATA, NDBM_File, $dbfile,O_CREAT|O_RDWR);
# Работаем с данными
...
untie(%DATA); # Завершаем работу с базой
О работе этих функций - perldoc -f dbmopen и perldoc -f tie.
Работа с хэшем DBM-базы не отличается от работы с обычным хэшем.
В принципе, этой информации о DBM-файлах достаточно.
Structured Query Language. Структурированный язык запросов, а попросту - SQL - это тема для отдельной статьи и только косвенно связана с Перл. Мы же рассмотрим способы взаимодействия с SQL-образными базами данных.
Самым удобным интерфейсом к БД является модуль DBI (нет в стандартной поставке Перл). DBI - это основной модуль интерфейса, к которому необходимо присовокупить (гусары, молчать!) драйвер для соответствующей базы данных. Не надо пугаться! Драйвер - это всего-лишь Perl-модуль. Для каждого типа БД модуль-драйвер будет иметь название DBD::basename: DBD::mysql, DBD::InterBase, DBD::mSQL и т.п.
Проще говоря, вам нужно просто установить два модуля: DBI и DBD::НазваниеБазы. Впоследствии, для других типов БД ставить DBI заново уже не требуется. Список имеющихся у вас драйверов можно получить так:
use DBI;
@driver_names = DBI->available_drivers;
Теперь о работе с DBI.
use DBI;
$db = DBI->connect("dbi:mysql:database=Users-;mysql_socket=/tmp/mysql_socket.sock","login","password") || die "Database Connection ERROR $DBI::errstr";
# Вот так мы подключимся к mysql-ной базе Users
$db = DBI->connect("dbi:InterBase:database=/home/db/data.gdb;ib_dialect=3;ib_charset=win1251","-login","pass")|| die "$DBI::errstr";
# А вот так - к базе формата InterBase
...
# Завершение
$db->disconnect();
Ошибки базы данных пишутся в DBI::errstr, откуда их можно запросто прочитать =).
Работа с данными после подключения происходит следующим образом.
$req=$db->do("CREATE TABLE USERS .......... "); # простые SQL-команды
$req=$db->do("DELETE FROM ORDERS"); # простые SQL- команды
$req=$db->do("DROP TABLE USERS"); # простые SQL- команды
# Более сложные команды типа SELECT, INSERT, и т.п.
# требуют другого подхода
$req=$db->prepare("SELECT NAMES FROM USERS");
$req->execute || die "$DBI::errstr";
$req->finish;
Метод finish вызывается для освобождения памяти, если ваш скрипт производит несколько запросов.
Для работы с данными в DBI имеется множество методов. Например:
$rv = $db->do($query);
# Просто запрос, результат которого окажется в $rv
Для получения данных после запроса (после prepare и execute) имееются различные методы.
@row = $req->fetchrow_array;
# Массив полей строки после SELECT
$req=$db->prepare("SELECT NAMES,LASTNAMES FROM USERS WHERE AGE>18");
$req->execute || die "$DBI::errstr";
while(@row=$req->fetchrow_array){
print "Имя: $row[0], Фамилия $row[1]\n";
}
$req->finish;
# Вот так, например, можно выбрать имена пользователей старше 18 лет.
$row_ref = $req->fetchrow_arrayref;
# Ссылка на массив полей строки после SELECT
# То же самое, но обращение к данным будет производится как $row_ref->[0], $row_ref->[1].
$hash_ref = $req->fetchrow_hashref;
# Ссылка на хэш полей строки после SELECT
# А вот так можно сложить выбранные строки в хэш, ссылкой на который будет
# $hash_ref.
# Если требуется выбрать одну строку, например случайного пользователя,
# то достаточно будет такого действия:
@row =$db->selectrow_array("SELECT NAME,LASTNAME FROM USERS ORDER BY RAND()");
# Здесь не требуется prepare, execute и т.п.
$row_ref = $db->selectrow_arrayref($query);
# То же, но в виде ссылки
$hash_ref = $db->selectrow_hashref($query);
# или хэш-ссылки
Можно сделать выборку в готовые переменные, которые выглядят нагляднее массивов и хэшей.
Для этого нужно воспользоваться методом bind_columns и связать данные со ссылками на соответствующие переменные.
$req=$db->prepare("SELECT NAMES,LASTNAMES FROM USERS WHERE AGE>18");
$req->execute || die "$DBI::errstr";
$req->bind_columns(\$Name, \$LastName);
while($req->fetch()){
print "Имя: $Name, Фамилия: $LastName\n";
}
$req->finish;
Список всех методов подробно описан в документации к DBI (perldoc DBI). Описание модуля, содержащееся в комплекте поставки, занимает около 150 килобайт. Подробнее документацию вы вряд ли найдете. Также полную информацию можно найти здесь: dbi.perl.org. Стоит еще упомянуть о расширениях модуля DBI. Они носят названия вида DBIx::***. Найти их можно на search.cpan.org.
Кроме модуля DBI существует масса отдельных модулей для разных типов БД. Например, Mysql - для MySQL, IBPerl - для InterBase. Однако, по моему мнению, DBI является самым удобным интерфейсом, а главное - он присутствует практически на всех хостингах.
Почему я не стал рассказывать подробно о работе с базами данных? Дело в том, что большинство проблем, возникающих при программировании под БД, появляется вследствие незнания принципов работы с БД, различий между различными их видами, что выходит за рамки изучения Перл. Ликбез по этой теме требует написания отдельных статей с рассмотрением конкретных типов БД. Я же не ставил перед собой такую задачу. Все, что нужно для программирования Perl+DB, - это книга по языку SQL, документация к вашей БД и perldoc DBI.
Небольшой совет. Запросы к БД следует составлять в отдельной переменной, и эту переменную передавать пераметром в do(), prepare(). Таким образом можно легко разобраться, если произойдет ошибка.
$query="SELECT NAMES,LASTNAMES FROM USERS WHERE AGE>18";
$req=$db->prepare($query);
$req->execute || die "$DBI::errstr Запрос:$query";
В следующей части статьи мы рассмотрим способы общения Perl-программ с пользователем, а именно - интерфейсы.
www.comprice.ru
Компьютерный справочник 02-12-2006
» Серия статей 14-03-2007 Компьютерный справочник Часть 1. Shale - это не Struts.В этой первой статье серии, автор рассказывает, что такое Shale, чем онотличается от среды Struts и как установить и настроить его в вашей средеразработки.Часть 2. Анатомия Shale-приложения. В данной статье я начну использовать принцип "проб и обучения" на практикеи подробно рассмотрю анатомию Shale-приложения. Я начну с так называемогоначального Shale-приложения (которое, как вы быстро поймете, явля...
» Adobe Photoshop Online 02-03-2007 Компьютерный справочник Компания Adobe планирует в ближайшее время запустить online вариант своего графического пакета Adobe Photoshop. Ближайшее время - это в течение ближайшего полугода. Так что скоро мы сможем редактировать свои изображения напрямую из браузера :-)
» Windows Mobile 6 - украдена 02-03-2007 Компьютерный справочник Появились сообщения о том, что в сети появилась пиратская версия Windows Mobile 6, которую компания Microsoft представила менее чем две недели назад на конференции 3GSM.
» iPhone интерфейс на PocketPC 01-03-2007 Компьютерный справочник Малазийский программист, используя язык программирования PPL, создал замену для экрана "Today" на PocketPC, который по своим функциям и внешнему виду очень похож на экран в Apple iPhone. Этот "клон" все еще в стадии разработки, однако им уже сейчас можно вполне пользоваться.Подробности
Вопрос-ответ 27-02-2007 Компьютерный справочник Уважаемые читатели! Мы продолжаем публикацию вопросов-ответов в рамках рубрики "F1-Help". Присылайте нам любые вопросы на компьютерную и телекоммуникационную тематику по адресу forum@comprice.ru, а мы постараемся вам ответить. Также по этому адресу вы можете оставлять свои пожелания в адрес нашего журнала и то, о чем бы вы хотели прочитать на его страницах.Вопрос: Не работает клавиатура PS2. Вообще нигде не работает, ни в Bios, ни в Windows. Кл...
» Статья: Установка большого Linux-кластера 27-02-2007 Компьютерный справочник Создайте работающий Linux-кластер из большого количества отдельных частейаппаратного и программного обеспечения, включая системы IBM System x и IBMTotalStorage.Часть 1. Введение и аппаратная конфигурация.Это первая часть серии статей,в которой описывается установка и настройка большого компьютерногоLinux-кластера. Целью данной статьи является объединение в одном местесамой новой информации из различных общедоступных источников о проце... |