| Сетевые клиенты |
|
Роль серверов - находиться в ожидании, когда клиенты соединятся с ними, а роль клиентов - инициировать соединения. В состав стандартной библиотеки языка Python входят реализации множества сетевых клиентов. В этом разделе мы обсудим наиболее типичные и часто используемые разновидности клиентов. socket Модуль socket реализует интерфейс доступа к реализации сокетов операционной системы. Это означает, что на языке Python можно реализовать любые действия с сокетами. На случай, если ранее вам не приходилось заниматься разработкой программ, работающих с сетью, Этот раздел предоставляет краткий обзор средств работы в сети. Это должно помочь вам составить представление о том, какие действия можно реализовать с помощью сетевых библиотек языка Python. Модуль socket содержит фабричную функцию socket(), которая создает и возвращает объект socket. Несмотря на то, что функция socket() может принимать большое число аргументов, определяющих тип создаваемого сокета, при вызове функции socket() без аргументов по умолчанию возвращается объект сокет TCP/IP:
В этом примере с помощью фабричной функции socket() создается объект типа socket с именем s. После этого он подключается к порту 80 (номер порта, используемый протоколом HTTP по умолчанию) локального веб-сервера. Затем серверу передается текстовая строка "GET / HTTP/ 1.0\n\n" (которая представляет собой простейший запрос HTTP). После посылки запроса сокет получает первые 200 байтов ответа сервера, в котором содержится сообщение о состоянии "200 ОК" и заголовки HTTP. В самом конце мы закрываем соединение. В этом примере демонстрируется использование методов объекта socket, к которым вы, вероятно, будете обращаться наиболее часто. Метод connect() устанавливает соединение между вашим объектом socket и удаленным сокетом (то есть «не с этим объектом сокета»). Метод send() выполняет передачу данных от вашего объекта socket удаленному хосту. Метод recv() выполняет прием любых данных, которые были переданы удаленным хостом. И метод close() закрывает соединение между двумя сокетами. Этот очень простой пример демонстрирует, насколько легко можно создавать объекты socket и с их помощью осуществлять передачу и прием данных. Теперь рассмотрим немного более полезный пример. Предположим, что у вас имеется сервер, на котором выполняется сетевое приложение, такое как веб-сервер. И вам необходимо следить за его работой, чтобы гарантировать возможность соединения с ним в течение дня. Это очень простой вид мониторинга, но он позволяет убедиться, что сервер продолжает работу и что веб-сервер по-прежнему ожидает соединений с клиентами на некотором порту. Взгляните на пример 5.1. Пример 5.1. Проверка порта TCP
Все необходимые действия выполняются функцией check_server(). Она создает объект socket. Затем пытается установить соединение с указанным номером порта по указанному адресу. Если соединение удалось установить, функция возвращает значение True. В случае неудачи метод socket. connect() возбуждает исключение, которое перехватывается функцией, и в этом случае она возвращает значение False. В разделе main этого сценария выполняется вызов функции check_server(). В этом разделе выполняется разбор аргументов командной строки, полученных от пользователя, и переданные аргументы преобразуются в соответствующий формат для последующей передачи функции check_ser-ver(). В процессе своей работы этот сценарий постоянно выводит сообщения о ходе выполнения. Самое последнее, что выводит сценарий, -это возвращаемое значение функции check_server(). В качестве собст- венного кода завершения сценарий возвращает командной оболочке значение, противоположное возвращаемому значению функции check_ serveг(). Сделано это для того, чтобы превратить сценарий в более или менее полезную утилиту. Обычно утилиты, подобные этой, возвращают командной оболочке значение 0 в случае успеха и некоторое другое значение, отличное от 0 (обычно некоторое положительное число), в случае неудачи. Ниже приводится пример вывода сценария, полученного при успешном соединении с веб-сервером, к которому мы уже подключались ранее:
Последняя строка в выводе, содержащая текст check_server returned True, означает, что соединение было благополучно установлено. Ниже приводится пример вывода сценария, полученного в результате неудачной попытки соединения:
Последняя строка в выводе, содержащая текст check_server returned False, означает, что попытка соединения не удалась. В предпоследней строке вывода, содержащей текст Connection to 192.168.1.15 on port 81 failed, можно увидеть причину: 'Connection refused' (Соединение отвергнуто). Об этом можно строить самые разнообразные предположения - например, возможно, что на данном сервере нет никаких процессов, обрабатывающих порт с номером 81. Мы создали три примера, чтобы продемонстрировать, как можно использовать эту утилиту в сценариях на языке командной оболочки. Первый пример представляет собой команду, которая запускает сценарий и выводит слово SUCCESS (успешно) в случае успеха. Здесь был использован оператор &&, играющий роль условной инструкции if-then:
В этом случае сценарий благополучно установил соединение, поэтому после того, как он выполнился и вывел результаты, командная оболочка напечатала слово SUCCES.
На этот раз сценарий завершился неудачей, но, несмотря на это, команда не вывела слово FAILURE (неудача).
В этом случае сценарий тоже потерпел неудачу, но на этот раз мы заменили оператор && оператором 11. Это просто означает, что в случае, если сценарий возвращает признак неудачи, следует вывести слово FAILURE, что и было сделано. Сам факт, что сервер позволяет выполнить подключение к порту с номером 80, еще не означает доступность веб-сервера. Более точно определить состояние веб-сервера поможет тест, который определяет, способен ли веб-сервер генерировать заголовки HTTP с ожидаемым кодом состояния для заданного URL. Сценарий в примере 5.2 реализует именно такой тест. Пример 5.2. Проверка веб-сервера с помощью сонетов
Как и в предыдущем примере, где основную работу выполняла функция check_server(), в этом примере также все необходимые действия выполняются единственной функцией check_webserver(). Сначала функция check_webserver() создает строку запроса HTTP. Протокол HTTP, если вы не в курсе, достаточно четко регламентирует порядок взаимодействий серверов и клиентов. Запрос HTTP, который создается в функции check_webserver(), является чуть ли не самым простым запросом. Затем функция check_webserver() создает объект socket, с его помощью устанавливает соединение с сервером и отправляет запрос HTTP. После этого она читает ответ сервера и закрывает соединение. Когда в сокете возникает ошибка, функция возвращает значение False, указывая тем самым, что проверка веб-сервера потерпела неудачу. Затем она берет ответ, полученный от сервера, и извлекает из него код состояния. Если код состояния имеет значение 200, что означает «ОК» (все в порядке), или 301, что означает «Moved Permanently» (запрошенная страница была перемещена), функция check_webserver() возвращает значение True, в противном случае она возвращает значение False. В разделе main сценарий выполняет разбор аргументов командной строки, полученных от пользователя, и вызывает функцию check_webserver(). После выполнения функции check_webserver() сценарий возвращает командной оболочке значение, противоположное значению, полученному от функции. Сделано это по тем же причинам, что и в предыдущем примере. Нам хотелось бы иметь возможность вызывать этот сценарий из сценариев командной оболочки и определять случаи успеха или неудачи. Ниже приводится пример использования этого сценария:
Последние четыре строки вывода показывают, что код состояния HTTP для адреса /apache2-default на этом сервере имеет значение 301, что означает успех.. Ниже приводится пример повторной попытки. На этот раз мы указали ресурс, отсутствующий на веб-сервере, чтобы продемонстрировать, что произойдет, когда функция check_webserver() вернет значение False:
Последние четыре строки в предыдущем примере свидетельствовали об успешной проверке, но в данном случае четыре последние строки вывода показывают, что проверка завершилась неудачей. На этом сервере отсутствует ресурс с адресом /foo, поэтому функция проверки вернула значение False. В этом разделе было показано, как создавать низкоуровневые утилиты, устанавливающие соединение с серверами в сети и выполняющие простейшие проверки. Цель этих примеров состояла в том, чтобы показать вам, как серверы и клиенты взаимодействуют друг с другом. Если у вас имеется возможность написать сетевой программный компонент, использующий более высокоуровневую библиотеку, чем модуль socket, вам следует использовать ее. Нет смысла тратить свое время на создание сетевых программных компонентов, использующих такую низкоуровневую библиотеку, как модуль socket. httplib Предыдущий пример продемонстрировал, как можно выполнить запрос HTTP с помощью модуля socket. В этом примере будет показано, как то же самое можно реализовать с помощью модуля httplib. Как определить, когда предпочтительнее использовать модуль httplib, а когда модуль socket? Или, в более широком смысле, когда предпочтительнее использовать более высокоуровневый модуль, а когда - более низкоуровневый? Одно хорошее правило, выработанное на практике, гласит - всякий раз, когда имеется такая возможность, следует использовать более высокоуровневый модуль. Возможно, вам потребуется нечто, что пока отсутствует в библиотеке, или вам необходимо организовать более точное управление чем-то, что уже имеется в библиотеке, или вы захотите воспользоваться преимуществами производительности. Но даже в этом случае нет никаких причин полностью отказываться от использования такой библиотеки, как httplib, и отдавать предпочтение такой низкоуровневой библиотеке, как socket. Пример 5.3 реализует ту же самую функциональность, что и предыдущий, используя для этого возможности модуля httplib. Пример 5.3. Проверка веб-сервера с помощью httplib
Концептуально этот пример достаточно близок к предыдущему. Два самых существенных отличия состоят в том, что здесь отсутствует необходимость вручную создавать строку запроса HTTP и нет никакой необходимости вручную выполнять анализ полученного ответа. Объ- ект соединения библиотеки httplib имеет метод request(), который конструирует и отправляет строку запроса HTTP. Кроме того, у объекта соединения имеется метод getresponse(), который создает объект с полученным ответом. Объект ответа предоставляет возможность получить код состояния ответа HTTP, обратившись к атрибуту status. Хотя объем программного кода в этом примере уменьшился ненамного, тем не менее, нам удалось избавиться от необходимости вручную создавать, отправлять и получать запрос и ответ HTTP. Да и выглядит этот пример более аккуратным. Ниже приводится результат запуска сценария с теми же аргументами командной строки, которые в предыдущем примере привели к успеху. Мы запрашиваем ресурс с адресом / на веб-сервере и находим его:
А ниже - результат запуска сценария с аргументами командной строки, которые в предыдущем примере привели к неудаче. Мы запрашиваем ресурс с адресом /foo на веб-сервере и не находим его:
Как уже говорилось выше, всякий раз, когда имеется возможность использовать более высокоуровневую библиотеку, следует использовать ее. Использование httplib вместо модуля socket оказалось более простым и более ясным. А чем проще программный код, тем ниже вероятность появления ошибок в нем. ftplib Помимо модулей httplib и socket в состав стандартной библиотеки языка Python входит также реализация клиента FTP в виде модуля ftplib. Модуль ftplib - это полнофункциональная клиентская библиотека для работы с протоколом FTP, которая позволяет реализовать любые задачи, выполняемые приложениями FTP-клиентов. Например, с ее помощью в сценарии на языке Python можно выполнить вход на FTP-сервер, получить список файлов в определенном каталоге, загрузить файлы, выгрузить файлы, перейти в другой каталог и выйти. Можно даже воспользоваться одной из множества платформ, предназначенных для реализации графического интерфейса, доступных в языке Python, и создать для работы с протоколом FTP свое приложение с графическим интерфейсом. Вместо того чтобы дать краткий обзор библиотеки, мы приведем пример 5.4 и затем поясним, как он работает. Пример 5.4. Загрузка файлов с помощью ftplib
В первой части сценария (сразу вслед за инструкциями, выполняющими разбор аргументов командной строки) создается объект FTP вызовом конструктора FTP(), которому передается адрес сервера. Можно было бы создать объект FTP, вызвав конструктор без аргументов, и затем вызвать метод connect(), передав ему адрес сервера FTP. Затем сценарий выполняет вход на сервер, используя имя пользователя и пароль, если таковые были указаны, в противном случае выполняется анонимный вход. Далее он создает объект типа file, куда будут сохраняться данные, получаемые из файла на сервере FTP. После этого вызывается метод retrbinary() объекта FTP. Метод retrbinary(), как следует из его имени, получает двоичный файл с сервера FTP. Метод принимает два параметра: команду, извлекающую файл, и функцию обратного вызова. Обратите внимание, что в качестве функции обратного вызова используется метод write() объекта file, который был создан на предыдущем шаге. Важно отметить, что в этом случае мы сами не вызываем метод writeQ. Мы передаем метод write() методу retrbinaryO, чтобы он сам мог вызывать метод write(). Метод retrbinary() будет вызывать функцию обратно вызова при получении каждого блока данных, получаемого от сервера FTP. Функция обратного вызова может выполнять над данными любые действия. Например, эта функция могла бы просто подсчитывать число байтов, принимаемых от сервера FTP. Передача метода write() объекта file приводит к тому, что данные, получаемые от сервера FTP, будут записаны в объект file. В заключение сценарий закрывает объект file и соединение с сервером FTP. В сценарии предусматривается обработка ошибок: процедура получения двоичного файла с сервера FTP заключена в инструкцию try, а в блоке finally выполняется закрытие локального файла и соединения с сервером FTP. Если случится что-то непредвиденное, файл и соединение будут закрыты перед завершением сценария. Краткое обсуждение функций обратного вызова вы найдете в приложении. urllib Перемещаясь ко все более высокоуровневым модулям, входящим в состав стандартной библиотеки, мы наконец достигли модуля urllib. Часто, рассматривая возможность применения библиотеки urllib, предполагают использовать ее для работы с протоколом HTTP, забывая, что ресурсы FTP также можно идентифицировать посредством URL. Поэтому вы, возможно, даже не предполагали, что библиотека urllib позволяет обращаться к ресурсам FTP, хотя такая возможность существует. Пример 5.5 обладает той же функциональностью, что и предыдущий пример, созданный на основе использования ftplib, но использует модуль urllib. Пример 5.5. Загрузка файлов с помощью urllib
Этот сценарий выглядит короче и опрятнее. Он наглядно демонстрирует мощь библиотеки urllib. На самом деле, большую часть сценария занимает документация, описывающая порядок его использования. Более того, даже для анализа аргументов командной строки потребовалось больше программного кода, чем для фактического выполнения действий. Мы решили упростить процедуру анализа аргументов командной строки. Поскольку оба аргумента являются обязательными, мы предпочли использовать позиционные аргументы и избавиться от ключей. По сути, всю работу в этом примере выполняет единственная строка:
После получения аргументов командной строки с помощью sys.argv эта строка обращается по указанному адресу URL и сохраняет полученные данные в локальном файле с указанным именем. Этот сценарий будет работать как с адресами HTTP, так и с адресами FTP, и будет работать, даже когда имя пользователя и пароль включены в URL. Ценность этого примера заключается в следующем: если вы предположили, что в языке Python некоторые действия должны выполняться проще, чем в других языках программирования, скорее всего так оно и окажется. Наверняка имеется какая-нибудь высокоуровневая библиотека, которая реализует именно то, что вам необходимо, и эта библиотека входит в состав стандартной библиотеки языка Python. В данном случае библиотека urllib реализует именно то, что необходимо, и оказалось достаточно заглянуть в документацию к стандартной библиотеке, чтобы узнать об этом. Впрочем, иногда вам, возможно, придется покидать стандартную библиотеку и искать другие ресурсы Python, такие как каталог пакетов Python (Python Package Index, PyPI) по адресу: http://pypi.python.org/pypi. urllib2 Примером другой высокоуровневой библиотеки является библиотека urllib2. Эта библиотека реализует практически те же самые функциональные возможности, что и библиотека urllib. Например, urllib2 обладает улучшенной поддержкой аутентификации и улучшенной поддержкой cookie. Поэтому, когда вы начнете использовать библиотеку urllib и обнаружите, что вам чего-то не хватает, обратитесь к библиотеке urllib2 - возможно, она лучше будет соответствовать вашим потребностям.
Related Articles
Set as favorite
Bookmark
Email This
Hits: 288 Комментарии (0)RSS feed CommentsНаписать комментарий |