Шрифт:
Разработка клиента и сервера
Мидлет Lighthouse использует все преимущества отношения «клиент – сервер» между двумя мобильными телефонами. Соединение между мидлетами – датаграммное, это означает, что обмен информацией будет производиться датаграммными пакетами. Поскольку при разработке мидлета Lighthouse используется концепция «клиент – сервер», необходимо знать, какой из телефонов инициирует соединение. Ниже перечислено, что происходит между телефоном-клиентом и телефоном-сервером в мидлете Lighthouse:
1. сервер начинает датаграммное соединение и ждет ответа клиента;
2. телефон-клиент открывает датаграммное соединение с телефоном-сервером;
3. когда соединение установлено, клиент и сервер обмениваются сообщениями;
4. клиент и сервер завершают соединение.
Интерес в разрабатываемом мидлете представляет то, что один из телефонов должен функционировать и как клиент, и как сервер в зависимости от контекста. Чтобы реализовать эту двойную функциональность, пользователь при запуске мидлета может определить, какую роль будет играть его телефон – клиента или сервера. После чего один телефон будет работать либо в режиме клиента, либо в режиме сервера. Зная, что для мидлета Lighthouse есть два режима функционирования, целесообразно разделить сетевой код мидлета на код клиента и код сервера.
В копилку Игрока
С точки зрения программирования сетевых игр пример Lighthouse является не настоящим примером приложения «клиент – сервер», а коммуникатором между двумя устройствами. В настоящих сетевых играх, основанных на концепции «клиент – сервер», есть отдельное серверное приложение, запущенное на сетевом сервере. Мидлеты, подключающиеся к серверу, – клиенты, а сервер управляет игрой. Мидлет Lighthouse является приложением «клиент – сервер» только лишь с той точки зрения, что одно устройство (сервер) ожидает подключения другого устройства (клиента).
Написание программного кода
Мидлет Lighthouse может работать в двух режимах – режиме клиента и режиме сервера. Режим определяется пользователем через интерфейс при запуске мидлета (вы это увидите чуть позже). Перед тем как вы перейдете к этому, важно разобрать код работы с сетью, который выполняет отправление и прием пакетов через беспроводное соединение.
Клиент и сервер мидлета Lighthouse
Код «клиент – сервер» в мидлете Lighthouse намного легче понять, если начать рассмотрение кода сервера. Все функции сервера содержатся в классе LHServer, который отвечает за ожидание датаграммного подключения клиента. Класс LHServer реализует интерфейс Runnable, что означает, что он запускается в отдельном потоке:
public class LHServer implements Runnable {
Это важно, поскольку класс запускает отдельный поток, отслеживающий соединение, и получает сообщения от клиента. Кроме сетевого соединения с клиентом, сервер также должен обмениваться информацией с холстом мидлета, который отображает маяк. Переменные класса LHServer говорят о некоторых его функциях:
private LHCanvas canvas;
private DatagramConnection dc;
private String address;
private Boolean connected;Холст хранится внутри класса LHServer в переменной canvas. Датаграммное соединение хранится в переменной dc – объекте класса DatagramConnection. Переменная address хранит адрес клиента, чтобы пакеты датаграммы могли быть направлены непосредственно получателю. И наконец, переменная connected отслеживает текущее состояние соединения с клиентом. Конструктор класса LHServer принимает единственный параметр – объект класса LHCanvas, конструктор выполняет ряд инициализаций:
public LHServer(LHCanvas c) {
canvas = c;
connected = false;
}Метод start также очень прост, он запускает поток:
public void start {
Thread t = new Thread(this);
t.start;
}Метод run – это метод, в котором реализуются основные функции сервера (листинг 14.1). Листинг 14.1. Метод run класса LHServer отвечает на сообщения, принятые от клиента
public void run {
try {
// соединиться с клиентским устройством
canvas.setStatus("Waiting for peer client..."); //Первое статусное сообщение сервера говорит о том, что он ожидает клиента
dc = null;
while (dc == null)
dc = (DatagramConnection)Connector.open("datagram://:5555"); //Порты клиента и сервера должны быть одинаковыми
while (true) {
// попробовать принять пакет датаграммы
Datagram dg = dc.newDatagram(32); //Размер датаграммы (32 байта) должен быть достаточно большим, чтобы вместить наибольшее возможное сообщение, однако в игре Lighthouse сообщения не очень велики
dc.receive(dg);
address = dg.getAddress;
// проверить, что датаграмма содержит данные
if (dg.getLength > 0) {
String data = new String(dg.getData, 0, dg.getLength);
if (data.equals("Client")) { //В ответ на соединение клиента, изменяется значение переменной и отправляется ответ
// оповестить пользователя об удачном соединении
canvas.setStatus("Connected to peer client.");
connected = true;
// попробовать ответить на принятое сообщение
sendMessage("Server");
}
else {
// отправить данные
canvas.receiveMessage(data); //Сообщение должно содержать знаки азбуки Морзе, поэтому необходимо его передать холсту
}
}
}
}
catch (IOException ioe) {
System.err.println("The network port is already taken.");
}
catch (Exception e) {
}
}Метод run начинается с вызова метода setStatus класса LHCanvas, который выводит в строку статуса холста «Waiting for peer client…» – режим ожидания клиента. Пользователь будет знать, что сервер ожидает подключения клиента. После того как статус выведен на холст, вызывается метод run, создающий датаграммное соединение. Номер использования порта (5555) – произвольный, однако важно, что клиент и сервер используют один порт для соединения. Также важно указать, что создаваемое соединение – датаграммное.
После того как датаграммное соединение установлено, метод run запускает бесконечный цикл, в котором выполняются попытки принятия пакетов от клиента. Сначала создается объект класса Datagram, а затем он используется как хранилище и приемник датаграмм. Адрес датаграммы сохраняется на тот случай, если серверу потребуется отправить ответ.
Если датаграмма содержит данные, то байты датаграммы преобразуются в строку. Затем проверяется, равна ли эта строка «Client», специальному сообщению, обозначающему соединение клиента с сервером. Если соединение прошло успешно, то статус изменяется и клиенту отправляется сообщение «Server», таким образом клиент уведомляется о том, что соединение установлено.
Датаграммный пакет содержит строку «Client» только в том случае, если соединение установлено впервые. Далее будут отправляться и приниматься пакеты, содержащие только слова «Dot» (точка) или «Dash» (тире), в зависимости от того, какое сообщение отправляется клиентом. Сообщение передается в класс LHCanvas, где оно обрабатывается методом receiveMessage. Подробнее об этом вы узнаете чуть позже, когда познакомитесь с кодом холста мидлета Lighthouse.
Последний метод класса LHServer – это метод sendMessage, приведенный в листинге 14.2. Этот метод отправляет сообщения клиенту.
Листинг 14.2. Метод sendMessage класса LHServer отправляет строковое сообщение как пакет датаграммыpublic void sendMessage(String message) {
// отправить сообщение
try {
// преобразовать текстовое сообщение в массив байтов
byte[] bytes = message.getBytes; //Строковое сообщение должно быть преобразовано в массив байтов
// отправить сообщение
Datagram dg = null; //Упаковка данных в датаграмму и отправка клиенту
dg = dc.newDatagram(bytes, bytes.length, address);
dc.send(dg);
}
catch (Exception e) {
}
}В этом коде строковое сообщение преобразуется в массив байтов, а затем отправляется клиенту как датаграммный пакет. Обратите внимание, что адрес, сохраненный ранее в методе run, теперь используется при создании объекта Datagram отправляемого сообщения. Этот адрес необходим, чтобы отправить сообщение клиенту. Однако, как вы увидите позже, этот адрес не обязателен при отправке сообщения клиентом серверу. Другая часть сетевого кода мидлета Lighthouse – это класс LHClient, который очень похож на класс LHServer. Так же, как и LHServer, класс LHClient также реализует интерфейс Runnable: