Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
лр3_Удаленное взаимодействие_Сокеты.doc
Скачиваний:
7
Добавлен:
20.08.2019
Размер:
3.66 Mб
Скачать

1.6. Пример многопоточного взаимодействия между приложениями

Программа состоит из приложения сервера и двух приложений клиентов. Приложение сервера позволяет приложению клиента подключиться к серверу и играть в известную игру "крестики – нолики". При соединении клиента с сервером создается экземпляр класса Player для обслуживания клиента в отдельном потоке управления. Это позволяет серверу управлять запросами обоих клиентов. Сервер присваивает значение "Х" первому клиенту, осуществившему соединение. Это означает, что игрок Х осуществил первый ход. Затем он присваивает значение "0" второму клиенту. По ходу игры сервер поддерживает информацию независимо от состояния игрового поля так, чтобы соблюдались правила выполнения ходов, запрошенных игроками. Однако, ни сервер, ни клиент не могут определить победу игроков, потому что в данной программе метод GameOver() всегда возвращает false. Приложение каждого клиента поддерживает свою версию интерфейса игрового поля "крестиков-ноликов". Игрок может ставить кресты или нули только на пустые клетки поля. Класс Square используется для описания клеток игрового поля.

1.6.1. Реализация серверной части.

В приложении сервера конструктор (стр.35-48) используется для создания массива байтов, сохраняющего сделанные игроками ходы (стр.39). Программа создает массив из двух ссылок на объекты Player (стр.41) и массив из двух ссылок на потоковые объекты типа Thread (стр.42). Каждый элемент обоих массивов соответствует игроку в "крестики-нолики". Переменная активного игрока currenPlayer имеет значение 0, соответствующее игроку "Х". В данной программе игрок "Х" делает первый ход (стр.43). В строках 46 и 47 создается и начинает исполняться объект getPlayer потока Thread, используемый объектом сервера, для приема соединений так, чтобы текущий поток не блокировал бы работу программы во время ожидания действия игроков.

Поток getPlayer исполяет метод SetUp (стр.65-92), создающий объект TcpListener для прослушивания запросов на порт 5000 (стр.68-69). Затем этот объект прослушивает запросы на соединение от первого и второго игроков. В стоках 72-73 и 79-80 создаются объекты типа Player, обозначающие игроков, а в строках 74-75 и 81-82 формируются два потока, исполняющие методы Run() каждого игрока.

Конструктор Player (стр.165-181) принимает в качестве аргумента ссылку на объект сокета (т.е. подключение к клиенту), ссылку на объект сервера и переменную целого типа, для обозначения игроков знаками ("Х" –крестик, "0" – нолик). В данном примере, после создания объекта игрока, приложение сервера вызывает метод Run() (стр.193-264). В строках 198-206 сервер уведомляется об успешном соединении и клиенту передается символ (char), который будет помещаться на игровое поле при очередном ходе. Если метод Run() исполняется для игрока "Х", то работают строки 211-221, и игрок "Х" ждет подключения второго игрока. В строках 217 и 218 определяется цикл while, приостанавливающий поток игрока "Х" до тех пор, пока сервер не просигнализирует о подключении игрока "0". Сервер уведомляет игрока о соединении, путем присвоения значения false переменной threadSuspend объекта игрока (стр.89). Когда threadSuspend принимает значение false, то объект игрока выходит из цикла (стр.217-218).

Метод Run() исполняет цикл (стр.226-256), что дает пользователю играть. Каждая итерация этого оператора ожидает, пока клиент отправит целочисленное значение, указывающее место на игровом поле для размещения знаков "Х" или "0". После этого игрок помещает знак на поле при допустимости указанного местоположения (например, данная клетка еще пустая). Цикл продолжает свою работу только, если логическая переменная done имеет ложное значение. Она устанавливается в истинное значение обработчиком события Server.Server_Closing(), который активизируется только при закрытии соединения сервером.