Программирование на языке Ruby - [202]
Есть еще вопрос о выборе цвета фигур. Оба партнера должны как-то договориться о том, кто каким цветом будет играть. Для простоты предположим, что цвет назначает сервер. Первый обратившийся клиент будет играть белыми (и, стало быть, ходить первым), второй — черными.
Уточним: компьютеры, которые первоначально были клиентами, начиная с этого момента общаются друг с другом напрямую; следовательно, один из них становится сервером. Но на эту семантическую тонкость я не буду обращать внимания.
Поскольку клиенты посылают запросы и ответы попеременно, причем сеанс связи включает много таких обменов, будем пользоваться протоколом TCP. Следовательно, клиент, который на самом деле играет роль «сервера», создает объект >TCPServer
, а клиент на другом конце — объект >TCPSocket
. Будем предполагать, что номер порта для обмена данными заранее известен обоим партнерам (разумеется, У каждого из них свой номер порта).
Мы только что описали простой протокол прикладного уровня. Его можно было бы сделать и более хитроумным.
Сначала рассмотрим код сервера (листинг 18.1). Чтобы его было проще запускать из командной строки, создадим поток, который завершит сервер при нажатии клавиши Enter. Сервер многопоточный — он может одновременно обслуживать нескольких клиентов. Данные о пользователях защищены мьютексом, ведь теоретически несколько потоков могут одновременно попытаться добавить новую запись в список.
>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
> end
> end
>end
>text = nil
>$server = TCPServer.new(HOST, PORT)
>while session = $server.accept do
> Thread.new(session) do |sess|
> text = sess.gets
> puts "Получено: #{text}" # Чтобы знать, что сервер получил.
> domain, port, ipname, ipaddr = sess.peeraddr
> handle_client sess, text, ipaddr, port, ipname
> sleep 1
> end
>end
>waiter.join # Выходим, когда была нажата клавиша Enter.
Метод >handle_client
сохраняет информацию о клиенте. Если запись о таком клиенте уже существует, то каждому клиенту посылается сообщение о том, где находится другой партнер. Этим обязанности сервера исчерпываются.
Клиент (листинг 18.2) оформлен в виде единственной программы. При первом запуске она становится TCP-сервером, а при втором — TCP-клиентом. Честно говоря, решение о том, что сервер будет играть белыми, совершенно произвольно. Вполне можно было бы реализовать приложение так, чтобы цвет не зависел от подобных деталей.
>require "socket"
>require "timeout"
>ChessServer = '96.97.98.99' # Заменить этот IP-адрес.
>ChessServerPort = 12000
>PeerPort = 12001
>WHITE, BLACK = 0, 1
>Colors = %w[White Black]
>def draw_board(board)
> puts <<-EOF
>+------------------------------+
>| Заглушка! Шахматная доска... |
>+------------------------------+
> EOF
>end
>def analyze_move(who, move, num, board)
> # Заглушка - черные всегда выигрывают на четвертом ходу.
> if who == BLACK and num == 4
> move << " Мат!"
> end
> true # Еще одна заглушка - любой ход считается допустимым.
>end
>def my_move(who, lastmove, num, board, sock)
> ok = false
> until ok do
> print "\nВаш ход: "
> move = STDIN.gets.chomp
> ok = analyze_move(who, move, num, board)
> puts "Недопустимый ход" if not ok
> end
> sock.puts move
> move
>end
>def other_move(who, move, num, board, sock)
> move = sock.gets.chomp
> puts "\nПротивник: #{move}"
> move
>end
>if ARGV[0]
> myself = ARGV[0]
>else
> print "Ваше имя? "
> myself = STDIN.gets.chomp
>end
>if ARGV[1]
> opponent_id = ARGV[1]
>else
> print "Ваш противник? "
> opponent_id = STDIN.gets.chomp
>end
>opponent = opponent_id.split(":")[0] # Удалить имя хоста.
># Обратиться к серверу
>socket = TCPSocket.new(ChessServer, ChessServerPort)
>response = nil
>socket.puts "login # {myself} #{opponent_id}"
>socket.flush
>response = socket.gets.chomp
В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.
Сейчас во многих школах, институтах и других учебных заведениях можно встретить компьютеры старого парка, уже отслужившие свое как морально, так и физически. На таких компьютерах можно изучать разве что Dos, что далеко от реалий сегодняшнего дня. К тому же у большинства, как правило, жесткий диск уже в нерабочем состоянии. Но и выбросить жалко, а новых никто не дает. Различные спонсоры, меценаты, бывает, подарят компьютер (один) и радуются, как дети. Спасибо, конечно, большое, но проблемы, как вы понимаете, этот компьютер в общем не решает, даже наоборот, усугубляет, работать на старых уже как-то не хочется, теперь просто есть с чем сравнивать.
В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.
В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.
В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.
Python - объектно-ориентированный язык сверхвысокого уровня. Python, в отличии от Java, не требует исключительно объектной ориентированности, но классы в Python так просто изучить и так удобно использовать, что даже новые и неискушенные пользователи быстро переходят на ОО-подход.