Чтение онлайн

на главную - закладки

Жанры

Программирование на языке Ruby
Шрифт:

Давайте напишем скелет шахматного сервера. Мы не имеем в виду программу, которая будет играть в шахматы с клиентом. Нет, наша задача — связать клиентов так, чтобы они могли затем играть без вмешательства сервера.

Предупреждаю, что ради простоты показанная ниже программа ничего не знает о шахматах. Логика игры просто заглушена, чтобы можно было сосредоточиться на сетевых аспектах.

Для установления соединения между клиентом и сервером будем использовать протокол TCP. Можно было бы остановиться и на UDP, но этот протокол ненадежен, и нам пришлось бы использовать тайм-ауты,

как в одном из примеров выше.

Клиент может передать два поля: свое имя и имя желательного противника. Для идентификации противника условимся записывать его имя в виде

user:hostname
; мы употребили двоеточие вместо напрашивающегося знака
@
, чтобы не вызывать ассоциаций с электронным адресом, каковым эта строка не является.

Когда от клиента приходит запрос, сервер сохраняет сведения о клиенте у себя в списке. Если поступили запросы от обоих клиентов, сервер посылает каждому из них сообщение; теперь у каждого клиента достаточно информации для установления связи с противником.

Есть еще вопрос о выборе цвета фигур. Оба партнера должны как-то договориться о том, кто каким цветом будет играть. Для простоты предположим, что цвет назначает сервер. Первый обратившийся клиент будет играть белыми (и, стало быть, ходить первым), второй — черными.

Уточним: компьютеры, которые первоначально были клиентами, начиная с этого момента общаются друг с другом напрямую; следовательно, один из них становится сервером. Но на эту семантическую тонкость я не буду обращать внимания.

Поскольку клиенты посылают запросы и ответы попеременно, причем сеанс связи включает много таких обменов, будем пользоваться протоколом TCP. Следовательно, клиент, который на самом деле играет роль «сервера», создает объект

TCPServer
, а клиент на другом конце — объект
TCPSocket
. Будем предполагать, что номер порта для обмена данными заранее известен обоим партнерам (разумеется, У каждого из них свой номер порта).

Мы только что описали простой протокол прикладного уровня. Его можно было бы сделать и более хитроумным.

Сначала рассмотрим код сервера (листинг 18.1). Чтобы его было проще запускать из командной строки, создадим поток, который завершит сервер при нажатии клавиши Enter. Сервер многопоточный — он может одновременно обслуживать нескольких клиентов. Данные о пользователях защищены мьютексом, ведь теоретически несколько потоков могут одновременно попытаться добавить новую запись в список.

Листинг 18.1. Шахматный сервер

require "thread"

require "socket"

PORT = 12000

HOST = "96.97.98.99" # Заменить этот IP-адрес.

# Выход при нажатии клавиши Enter.

waiter = Thread.new do

 puts "Нажмите Enter для завершения сервера."

 gets

 exit

end

$mutex = Mutex.new

$list = {}

def match?(p1, p2)

 return false if !$list[p1] or !$list[p2]

 if ($list[p1][0] == p2 and $list[p2][0] == p1)

true

 else

false

 end

end

def handle_client(sess, msg, addr, port, ipname)

 $mutex.synchronize do

cmd, player1, player2 = msg.split

#
Примечание: от клиента мы получаем данные в виде user:hostname,

# но храним их в виде user:address.

p1short = player1.dup # Короткие имена

p2short = player2.split(":")[0] # (то есть не ":address").

player1 << ":#{addr}" # Добавить IP-адрес клиента.

user2, host2 = player2.split(":")

host2 = ipname if host2 == nil

player2 = user2 + ":" + IPSocket.getaddress(host2)

if cmd != "login"

puts "Ошибка протокола: клиент послал сообщение #{msg}."

end

$list[player1] = [player2, addr, port, ipname, sess]

if match?(player1, player2)

# Имена теперь переставлены: если мы попали сюда, значит

# player2 зарегистрировался первым.

p1 = $list[player1]

р2 = $list[player2]

# ID игрока = name:ipname:color

# Цвет: 0=белый, 1=черный

p1id = "#{p1short}:#{p1[3]}:1"

p2id = "#{p2short}:#{p2[3]}:0"

sess1 = p1[4]

sess2 = p2[4]

sess1.puts "#{p2id}"

sess2.puts "#{p1id}"

sess1.close

sess2.close

Поделиться:
Популярные книги

Криптономикон, часть 2

Стивенсон Нил
Фантастика:
киберпанк
5.00
рейтинг книги
Криптономикон, часть 2

Попаданец на рыбалке. Книги 1-7

Скворцов Владимир Николаевич
Попаданец на рыбалке
Фантастика:
фэнтези
попаданцы
6.88
рейтинг книги
Попаданец на рыбалке. Книги 1-7

Юся и эльф

Демина Карина
Фантастика:
юмористическое фэнтези
детективная фантастика
6.25
рейтинг книги
Юся и эльф

Аврора Горелика (сборник)

Аксенов Василий Павлович
Поэзия:
драматургия
6.25
рейтинг книги
Аврора Горелика (сборник)

А.и Б. Стругацкие. Собрание сочинений в 10 томах. Т.10

Стругацкие Аркадий и Борис
10. Собраниния сочинений в 10 томах
Фантастика:
научная фантастика
6.71
рейтинг книги
А.и Б. Стругацкие. Собрание сочинений в 10 томах. Т.10

Алчность. Жажда магии

Post Mort
Фантастика:
фэнтези
5.00
рейтинг книги
Алчность. Жажда магии

Одиссея капитана Балка. Дилогия

Чернов Александр Викторович
Фантастика:
боевая фантастика
альтернативная история
5.25
рейтинг книги
Одиссея капитана Балка. Дилогия

Гримуар темного лорда VI

Грехов Тимофей
6. Гримуар темного лорда
Фантастика:
попаданцы
аниме
фэнтези
5.25
рейтинг книги
Гримуар темного лорда VI

Благие намерения

Лукина Любовь Александровна
Фантастика:
фэнтези
научная фантастика
7.50
рейтинг книги
Благие намерения

Отморозок 2

Поповский Андрей Владимирович
2. Отморозок
Фантастика:
попаданцы
5.50
рейтинг книги
Отморозок 2

Газлайтер. Том 29

Володин Григорий Григорьевич
29. История Телепата
Фантастика:
боевая фантастика
попаданцы
аниме
6.40
рейтинг книги
Газлайтер. Том 29

Дарт Вейдер. Ученик Дарта Сидиуса

Jamique
Звездные войны
Фантастика:
социально-философская фантастика
космическая фантастика
6.00
рейтинг книги
Дарт Вейдер. Ученик Дарта Сидиуса

Призрак страсти

Эрскин Барбара
Любовные романы:
любовно-фантастические романы
8.60
рейтинг книги
Призрак страсти

Толковая Библия. Ветхий Завет. Книги пророческие.

Лопухин Александр Павлович
Религия и эзотерика:
религия
5.00
рейтинг книги
Толковая Библия. Ветхий Завет. Книги пророческие.