Статьи Perl и работа с сокетами

Discussion in 'Статьи' started by LoFFi, 10 May 2006.

  1. LoFFi

    LoFFi Elder - Старейшина

    Joined:
    21 Feb 2006
    Messages:
    194
    Likes Received:
    90
    Reputations:
    85
    Иногда возникает необходимость из программы на перле соединиться с
    другим сервером и передать/принять какую-нибудь информацию. Как
    правило, возникает необходимость работать с протоколом HTTP, но поняв
    основные принципы взаимодействия между двумя серверами и прочитав
    спецификацию интересующего протокола, можно написать программу,
    работающую практически через любой протокол. Далее мы рассмотрим
    работу с протоколом HTTP.

    Первое что нам понадобится это - сокеты (sockets). Сокет - это канал,
    проложенный между сервером на котором запускается программа и
    сервером, с которым мы хотим установить соединение. Для работы с
    сокетами в перле есть модуль - Socket.
    Для создания сокета используется функция socket. Формат ее таков:

    socket(SOCK, DOMAIN, TYPE, PROTOCOL);


    Данная функция открывает сокет и привязывает его к указателю SOCK.

    DOMAIN - это коммуникационный домен. Не нужно его путать с доменом
    сервера с которым мы соединяемся. В нашем случае это будет Internet
    домен (бывает еще Unix домен) а потому указываем: PF_INET

    TYPE - это тип сокета. Мы будем использовать SOCK_STREAM - этот тип
    обеспечивает последовательный, надежный поток байтов. Так же
    существуют Datagram socket и Raw socket, но о них как-нибудь в другой
    раз.

    PROTOCOL - протокол, по которому будет устанавливаться соединение. В
    нашем случае это tcp поэтому вместо PROTOCOL вставляем следующее:

    getprotobyname('tcp');


    Помимо tcp можно использовать udp, ip и т.д. Функция getprotobyname
    возвращает название протокола в более удобном для функции socket виде.

    Итак создаем сокет:

    socket(SOCK, PF_INET, SOCK_STREAM, getprotobyname('tcp'));


    Когда сокет готов, можно подключаться к конкретному серверу. Для этого
    нам нужен адрес сервера и порт. Предварительно, необходимо сделать
    следующее:

    # Конвертирует имя сервера в бинарную последовательность.
    $iaddr = inet_aton($host);
    # Упаковывает все в понятную функции connect последовательность.
    $paddr = sockaddr_in($port, $iaddr);


    Теперь все готово и мы можем использовать функцию connect:

    connect(SOCK, $paddr);


    После соединения с сервером, мы можем передавать и принимать некоторую
    информацию. Рассмотрим процесс передачи и приема данных, более
    подробно, на примере соединения с Web-сервером и получения с него
    некоторого документа. Для отправки сообщения через сокет служит
    функция send:

    send (SOCK, "То что шлем", 0);


    Вместо 0 может быть один из перечисленных флагов:

    MSG_OOB - Посылать/получать данные, характерные для сокетов типа SOCK_STREAM
    MSG_DONTROUTE - Посылать данные без маршрутизации пакетов. Как правило
    используется диагностическими программами и процессами управляющими
    таблицами маршрутизации.

    Для приема данных через сокет, используем стандартную операцию:

    @data=<SOCK>;


    После окончания сеанса связи, необходимо закрыть сокет, при этом
    серверу сообщается что сеанс связи закончен. Для закрытия сокета
    служит функция close, которой необходимо передать дескриптор сокета.

    close(SOCK);


    Ну и на последок пример рабочего кода. Программа соединяется с
    сервером http://www.perl.ru и забирает от туда главную страницу.

    use Socket;

    $host="www.perl.ru";
    $port="80";

    socket(SOCK, PF_INET, SOCK_STREAM, getprotobyname('tcp'));

    $iaddr = inet_aton($host);
    $paddr = sockaddr_in($port, $iaddr);

    connect(SOCK, $paddr);
    send (SOCK, "GET / HTTP/1.0\n\n", 0);

    @data=<SOCK>;

    close(SOCK);

    print @data;


    Если вы попробуете поменять значения переменной $host, то обнаружите,
    что с некоторых серверов приходит сообщение об ошибке. Всему причиной
    HTTP протокол и настройки Web-серверов. Более подробно о HTTP читайте
    в статье "HTTP протокол". А пока о том как с этим бороться.

    Дело в том, что в сети существует очень большое количество виртуальных
    серверов, т.е. серверов с разными именами, но одним IP адресом.
    Попробуйте например сделать Ping Любое_имя.narod.ru. Все они будут
    иметь один IP адрес. А поскольку наш сокет фактически соединяется с IP
    адресом, то мы и получаем сообщение об ошибке. Web-сервер того же
    narod.ru просто не знает страницы какого из виртуальных серверов
    показывать. Значит нужно объяснить ему это. Для этого существует
    переменная HOST которая указывается в заголовке запроса. Т.е.

    send (SOCK, "GET / HTTP/1.0\nHOST:$host\n\n", 0);


    Подставив эту строчку в наш скрипт, мы получим уже более совершенную
    программу. Так же, некоторые сервера в зависимости от браузера (а вы
    еще не ощущаете себя им?) показывают разные версии сайтов. Встречается
    такое крайне редко, но к этому нужно быть готовым. Используйте для них
    переменную USER-AGENT.

    =================================
    Источник: http://www.opennet.ru/base/dev/perl_socket2.txt.html