Программирование на языке Ruby - [201]
Класс >Net::Protocol
, являющийся родительским для классов HTTP, POP3 и SMTP полезен скорее для разработки новых сетевых протоколов, но эта тема в данной книге не обсуждается.
На этом завершим краткий обзор и приступим к рассмотрению низкоуровневого сетевого программирования.
18.1. Сетевые серверы
Жизнь сервера проходит в ожидании входных сообщений и ответах на них.
Не исключено, что для формирования ответа требуется серьезная обработка, например обращение к базе данных, но с точки зрения сетевого взаимодействия сервер просто принимает запросы и отправляет ответы.
Но даже это можно организовать разными способами. Сервер может в каждый момент времени обслуживать только один запрос или иметь несколько потоков. Первый подход проще реализовать, зато у второго есть преимущества, когда много клиентов одновременно обращается с запросами.
Можно представить себе сервер, единственное назначение которого состоит в том, чтобы облегчить общение между клиентами. Классические примеры — чат-серверы, игровые серверы и файлообменные сети.
18.1.1. Простой сервер: время дня
Рассмотрим самый простой сервер, который вы только способны представить. Пусть некоторая машина располагает такими точными часами, что ее можно использовать в качестве стандарта времени. Такие серверы, конечно, существуют, но взаимодействуют не по тому тривиальному протоколу, который мы обсудим ниже. (В разделе 18.2.2 приведен пример обращения к подобному серверу по протоколу telnet.)
В нашем примере все запросы обслуживаются в порядке поступления однопоточным сервером. Когда приходит запрос от клиента, мы возвращаем строку, содержащую текущее время. Ниже приведен код сервера:
>require "socket"
>PORT = 12321
>HOST = ARGV[0] || 'localhost'
>server = UDPSocket.open # Применяется протокол UDP...
>server.bind nil, PORT
>loop do
> text, sender = server.recvfrom(1)
> server.send(Time.new.to_s + "\n", 0, sender[3], sender[1])
>end
А это код клиента:
>require "socket"
>require "timeout"
>PORT = 12321
>HOST = ARGV[0] || 'localhost'
>socket = UDPSocket.new
>socket.connect(HOST, PORT)
>socket.send("", 0)
>timeout(10) do
> time = socket.gets
> puts time
>end
Чтобы сделать запрос, клиент посылает пустой пакет. Поскольку протокол UDP ненадежен, то, не получив ответа в течение некоторого времени, мы завершаем работу по тайм-ауту.
В следующем примере такой же сервер реализован на базе протокола TCP. Он прослушивает порт 12321; запросы к этому порту можно посылать с помощью программы telnet (или клиента, код которого приведен ниже).
>require "socket"
>PORT = 12321
>server = TCPServer.new(PORT)
>while (session = server.accept)
> session.puts Time.new
> session.close
>end
Обратите внимание, как просто использовать класс >TCPServer
. Вот TCP-версия клиента:
>require "socket"
>PORT = 12321
>HOST = ARGV[0] || "localhost"
>session = TCPSocket.new(HOST, PORT)
>time = session.gets
>session.close
>puts time
18.1.2. Реализация многопоточного сервера
Некоторые серверы должны обслуживать очень интенсивный поток запросов. В таком случае эффективнее обрабатывать каждый запрос в отдельном потоке.
Ниже показана реализация сервера текущего времени, с которым мы познакомились в предыдущем разделе. Он работает по протоколу TCP и создает новый поток для каждого запроса.
>require "socket"
>PORT = 12321
>server = TCPServer.new(PORT)
>while (session = server.accept)
> Thread.new(session) do |my_session|
> my_session.puts Time.new
> my_session.close
> end
>end
Многопоточность позволяет достичь высокого параллелизма. Вызывать метод >join
не нужно, поскольку сервер исполняет бесконечный цикл, пока его не остановят вручную.
Код клиента, конечно, остался тем же самым. С точки зрения клиента, поведение сервера не изменилось (разве что он стал более надежным).
18.1.3. Пример: сервер для игры в шахматы по сети
Не всегда нашей конечной целью является взаимодействие с самим сервером. Иногда сервер — всего лишь средство для соединения клиентов друг с другом. В качестве примера можно привести файлообменные сети, столь популярные в 2001 году. Другой пример — серверы для мгновенной передачи сообщений, например ICQ, и разного рода игровые серверы.
Давайте напишем скелет шахматного сервера. Мы не имеем в виду программу, которая будет играть в шахматы с клиентом. Нет, наша задача — связать клиентов так, чтобы они могли затем играть без вмешательства сервера.
Предупреждаю, что ради простоты показанная ниже программа ничего не знает о шахматах. Логика игры просто заглушена, чтобы можно было сосредоточиться на сетевых аспектах.
Для установления соединения между клиентом и сервером будем использовать протокол TCP. Можно было бы остановиться и на UDP, но этот протокол ненадежен, и нам пришлось бы использовать тайм-ауты, как в одном из примеров выше.
Клиент может передать два поля: свое имя и имя желательного противника. Для идентификации противника условимся записывать его имя в виде >user:hostname
; мы употребили двоеточие вместо напрашивающегося знака >@
, чтобы не вызывать ассоциаций с электронным адресом, каковым эта строка не является.
Когда от клиента приходит запрос, сервер сохраняет сведения о клиенте у себя в списке. Если поступили запросы от обоих клиентов, сервер посылает каждому из них сообщение; теперь у каждого клиента достаточно информации для установления связи с противником.
В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.
Сейчас во многих школах, институтах и других учебных заведениях можно встретить компьютеры старого парка, уже отслужившие свое как морально, так и физически. На таких компьютерах можно изучать разве что Dos, что далеко от реалий сегодняшнего дня. К тому же у большинства, как правило, жесткий диск уже в нерабочем состоянии. Но и выбросить жалко, а новых никто не дает. Различные спонсоры, меценаты, бывает, подарят компьютер (один) и радуются, как дети. Спасибо, конечно, большое, но проблемы, как вы понимаете, этот компьютер в общем не решает, даже наоборот, усугубляет, работать на старых уже как-то не хочется, теперь просто есть с чем сравнивать.
В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.
В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.
В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.
Python - объектно-ориентированный язык сверхвысокого уровня. Python, в отличии от Java, не требует исключительно объектной ориентированности, но классы в Python так просто изучить и так удобно использовать, что даже новые и неискушенные пользователи быстро переходят на ОО-подход.