СРМТ_КП

advertisement
Задание к курсовому проекту по дисциплине
”Спецразделы мультимедиатехнологий”
Организовать
сетевое
взаимодействие
между
двумя
мобильными
устройствами У1 и У2. Четные варианты используют для обмена протокол TCP,
нечетные – протокол UDP. Устройство У1 выступает в роли клиента, а У2 в роли
сервера. Схема взаимодействия приведена на рис.1. При этом У1 подготавливает
исходные данные и отображает результаты обработки, а вся обработка выполняется
в У2.
У2
У1
исходные данные
Подготовка
исходных данных
и отображение
результатов
результаты
Обработка
Рис.1 – Схема сетевого взаимодействия
Варианты заданий
1,2. У2 выполняет роль ОЗУ (оперативного запоминающего устройства)
емкостью 16 байт,
реализует операции чтения и записи,
формирует сигнал
готовности по завершении операции, отображает содержимое банка памяти. У1
выставляет адрес на шину адреса, данные на шину данных при операции запись,
сигнал записи или чтения на шину управления, отображает содержимое шин адреса,
данных и управления.
3,4. У2 выполняет роль переводчика чисел из систем счисления с основаниями
(10), (8) и (16) в двоичную систему счисления и отображает реализуемое действие.
У1 подготавливает исходные данные для перевода и отображает
8-битный
результат на светодиодах. Исходные данные – числа без знака в диапазоне байта: 0 –
255(10), 0 – 377(8), 0 – FF(16).
5,6. У2 выполняет роль переводчика чисел из двоичной системы счисления в
системы счисления с основаниями (10), (8) и (16) и отображает реализуемое
действие. У1 подготавливает исходные данные для перевода и отображает
результат на индикаторном табло (3 семисегментных
индикатора).. Исходные
данные – числа без знака в диапазоне байта: 00000000 – 11111111(2).
7,8. У2 выполняет роль устройства сортировки массива, отображает исходный
массив. У1 подготавливает исходный массив для сортировки, задает направление
сортировки и отображает результат сортировки.
9,10. У2 выполняет роль генератора случайных чисел. У1 может задавать сам, а
может запрашивать у У2 следующую информацию:
 фигура (круг или квадрат);
 цвет фигуры;
 размер (радиус или сторона квадрата);
 координаты начального месторасположения;
 направление перемещения фигуры (влево, вправо, вверх, вниз);
 скорость перемещения;
 действие при достижении границы (выход, отражение). При этом могут
запрашиваться или некоторые из параметров или все параметры.
Отображение
выполняется на экране устройства У1.
11,12. У2 отображает исходное изображение и выполняет роль устройства
трансформации изображения. У1 готовит исходное изображение, задает тип
трансформации и отображает повернутое изображение.
Пример разработки
Разработка сетевого мобильного приложения по архитектуре “клиент-сервер”
между двумя мобильными устройствами. При этом любое устройство
может
выполнять как функции клиента, так функции сервера. Соединение между
устройствами реализуется либо по протоколу UDP, либо по протоколу TCP. В
качестве мобильных устройств выберем мобильные телефоны, как наиболее
распространенное на сегодняшний день средство коммуникации.
В качестве примера рассмотрим следующую задачу. Необходимо реализовать
простейший калькулятор, выполняющий четыре элементарных арифметических
действия: сложение, вычитание, умножение и деление. Вычисления производятся
над четырехразрядными десятичными целыми числами со знаком. Результат может
содержать до восьми разрядов со знаком. При этом задача клиента подготовить
исходные операнды для вычислений и задать команду, которую необходимо
выполнить. Для ввода используется клавиатура телефона, а результаты вычислений
отображаются на экране
телефона. Сервер выполняет роль арифметического
устройства и реализует следующие функции:
 - получение операндов и операции от клиента;
 - выполнение требуемой операции;
 - передача результата операции клиенту.
Для работы приложения необходимо определить форматы передаваемых
сообщений. Формат сообщения, передаваемого клиентом серверу, представлен на
рис.2.
знак
1
операнд 1
знак
операнд 2
операция
4
1
4
1
11 символов
Рис.2 – Формат сообщения клиента
Формат имеет фиксированную длину 11 символов, чтобы дополнительно не
включать в сообщение длины полей операндов и упростить серверу разбор
принятого сообщения. Если операнды имеют длину меньшую, чем 4 символа, то в
старшие позиции поля вставляется символ “0”.
Поле знака операнда содержит
символ “-“, если операнд отрицательный и символ ”0”, в противном случае.
Сообщение сервера содержит только результат выполнения арифметической
операции и может содержать от 1 до 9 символов.
Сокетное соединение
Программа
сетевого соединения с использованием протокола TCP/IP
приведена в листинге 1. Приложение состоит из четырех классов. Класс
NetCalcSocketMIDlet – класс мидлета, класс NCCanvas – класс холста устройств,
класс NCClient – класс функционирования устройства сокетного клиента, NCServer
– класс устройства сокетного сервера.
Сервер мидлета NetCalcSocketMIDlet
Класс NCServer реализует интерфейс Runnable. Он запускается в отдельном
потоке, который отслеживает сетевое соединение с клиентом и отображает
информацию в строке состояния холста мидлета.
Класс имеет следующие переменные. Ссылка на холст хранится в переменной
canvas. Переменная is – ссылка на входной поток InputStream, а os – на выходной
поток OutputStream. sc и scn – ссылки на объекты интерфейсов SocketConnection
ServerSocketConnection соответственно.
Переменные
closing_rcv
и
closing_snd
сигнализируют
о
завершении
соединения между сервером и клиентом. Сервер присваивает переменной
closing_rcv значение true при получении сообщения ”stop” от клиента. Если
соединение завершается по инициативе сервера, то он посылает сообщение ”stop”
клиенту и записывает значение true в переменную closing_snd.
Метод run() – метод, в котором реализуются основные функции сервера:
 в строку состояния холста выводится сообщение ”Waiting for peer client…” об
ожидании подключения клиента;
 сервер открывает соединение, вызывая метод Connector.open(“socket://:5000”)
возвращая объект scn интерфейса ServerSocketConnection;
 сервер прослушивает возможные запросы соединения от клиентов, используя
метод scn.acceptAndOpen(), который блокирует операции до тех пор пока не
появится запрос соединения от клиента. При появлении запроса этот метод
принимает запрос, создает новый объект соединения sc , связывает соединение с
неиспользуемым сокетом и уведомляет клиента о новом соединении;
 в строку состояния выводится cообщение об установленном соединении;
 открывает входной и выходной потоки для соединения;
 в бесконечном цикле принимаются сообщения клиента, используя метод
receiveMessage(InputStream) класса NCCanvas и формируется ответное сообщение.
Цикл работает до получения сообщения ”stop” от клиента.
В методе decodeAndOperation(String) сервер разбирает полученное от клиента
сообщение,
выполняет
операцию
над
заданными
аргументами,
результат
преобразуеи в строку и отображает информацию в строке состояния холста мидлета.
Метод sendMessage(OutputStream, String) преобразует строку в байтовый
массив, который записывается в выходной поток outputStream.
Метод close() проверяет значения переменных closing_rcv и closing_snd. Если
обе переменные принимают значение true, то закрываются все открытые потоки.
Клиент мидлета NetCalcSocketMIDlet
Другая часть сетевого кода мидлета NetCalcSocketMIDlet – это класс NCClient,
который очень похож на класс NCServer.
Класс NCClient также реализует
интерфейс Runnable. Переменные класса NCClient совпадают с переменными класса
NCServer за исключением переменной scn, которая используется только в серверной
части соединения. Метод run() класса клиента отличается от подобного метода в
классе сервера и реализует следующие действия:
 клиент запрашивает соединение у известного сокета, создавая клиентский
запрос при помощи метода Connector.open( "socket://localhost:5000" ). В запросе
должно быть указано имя сервера (в примере, localhost) и номер порта (в примере,
5000), который обязательно должен совпадать с портом сервера;
 после установления соединения с сервером в строку состояния холста
выводится сообщение "Connected to peer server." ;
 открывает входной и выходной потоки для соединения;
 в бесконечном цикле принимаются сообщения сервера, используя метод
receiveMessage(InputStream) класса NCCanvas. Цикл работает до получения
сообщения ”stop” от сервера. Для посылки сообщений серверу, используется метод
sendMessage1( String), который вызывается из класса NCCanvas.
Холст мидлета NetCalcSocketMIDlet
После создания классов сервера и клиента создается класс NCCanvas. Он
выводит в верхней части экрана картинку для сервера или клиента, а в нижней –
строку состояния. Строка состояния отображает информацию об установлении и
завершении соединения между устройствами, а также информацию о выполняемых
вычислениях.
Кратко опишем переменные и методы класса NCCanvas.
Переменная d хранит ссылку на дисплей и используется для отображения
объектов в методе commandAction(Command, Displayable).
(ссылка
на родительский
класс мидлета)
служит
для
Переменная parent
вызова
методов
destroyApp(true) и notifyDestroyed() при завершении работы приложения.
Переменные client и server – это объекты классов NCClient и NCServer
соответственно. Они отвечают за работу мидлета с сетью. Переменная isClient
определяет режим работы мидлета. Если она принимает значение true, то мидлет
работает в режиме клиента, в противном случае – в режиме сервера. Для ввода
значений аргументов в режиме клиента используется форма, на которую ссылается
переменная
form_num.
Значения аргументов в строковом представлении
сохраняются в переменных first_arg и second_arg. Переменная full_flag отвечает за
заполнение полей ввода аргументов в форме.
Для прекращения работы потока
класса NCCanvas используется переменная sleeping. При старте потока в методе
start() ей присваивается значение false, а в методе stop() (остановка потока) –
значение true.
Изображения для клиента и сервера содержатся в переменных
ImageClient и ImageServer соответственно.
В переменной
выполняемой
arguments хранится сообщение клиента без указания
операции.
Для
текста,
отображаемого
в
строке
состояния,
используется переменная status.
В методе start():
 инициализируются фоновые изображения клиента и сервера;
 в зависимости от значения переменной isClient создается экземпляр класса
NCClient или NCServer ;
 в режиме “клиент” добавляется команда ”Numbers” (вызов формы для
ввода чисел);
 вызывается метод start(), запускающий поток соединения;
 добавляются команды “Close” (закрытие соединения) и “Exit”(выход из
приложения);
 стартует поток класса NCCanvas.
Метод run() запускает поток класса NCCanvas на выполнение. Он содержит в
себе игровой цикл, в котором
вызываются методы update() (обработка
пользовательского ввода с клавиатуры) и draw() (отображение информации на
дисплее). Задержка потока на 1,5 секунды позволяет
наблюдать процесс
взаимодействия клиента и сервера по отображению строки состояния на холстах
этих устройств.
Метод update() проверяет нажатие клавиш и
работает только в режиме
“клиент” при заданных значениях аргументов. Пользователь задает выполняемую
команду (операцию) нажимая соответствующую клавишу (табл.1).
Таблица 1 - Соответствие клавиш и выполняемых операций
Клавиша
Действие
Влево
Сложение
Вправо
Вычитание
Вверх
Умножение
Вниз
Деление
К строке arguments добавляется знак операции, и сформированное сообщение
посылается серверу при помощи метода sendMessage1(String) класса NCClient.
Информация о выполняемой операции отображается в строке состояния
клиента.
Метод draw() отображает картинку для сервера или клиента и выводит строку
состояния. Для доступа к строке состояния объектов классов NCClient и NCServer
используются открытые методы setStatus(String) – записать текст в строку состояния
и getStatus() – прочитать содержимое строки состояния.
В методе CommandAction(Command, Displayable) происходит обработка
команд приложения. Команда ”Numbers”
создает форму для ввода значений
аргументов, добавляет к форме команды “Enter” и “Cancel” и отображает форму на
дисплее. Команда ”Enter” проверяет заполнены ли поля ввода аргументов и не
превышают ли значения аргументов величины 9999, т.к. в полях ввода возможен
ввод 5 символов. Для отрицательных чисел проверка не выполняется, т.к. старшим
символом является знак '-'
и возможен ввод только четырех символов. При
обнаружении ошибок выводятся соответствующие ошибки. При отсутствии ошибок
вызывается метод formString(TextField, TextField), формирующий строку сообщения
клиента из двух аргументов. Информация о введенных значениях аргументов
отображается
в
строке
состояния
клиента.
Команда
”Cancel”
отменяет
формирование новых аргументов в форме ”Numbers”. Команда ”Close” завершает
соединение, вызывая метод stop(), удаляет команду приложения “Close” и команду
“Numbers” в режиме “клиент”. Команда ”Exit” – команда выхода из приложения.
Метод removeNumbersCommand() предназначен для удаления команды
”Numbers” из класса NCClient. Метод receiveMessage(InputStream) читает сообщение
из входного потока и записывает его в строку. Метод работает одинаково как для
клиента, так и для сервера. Чтобы не включать его как в класс NCClient, так и в
класс NCServer, он
содержится в классе NCCanvas, а вызывается из классов
NCClient NCServer, используя ссылку на класс NCCanvas.
Мидлет NetCalcSocketMIDlet
Класс NetCalcSocketMIDlet реализует пользовательский интерфейс для выбора
режима работы устройства (клиент или сервер). Для этого создается форма “Net
calc”, в которую включается группа выбора ChoiceGroup, предлагающая два
варианта выбора ”Server” или “Client”. К форме добавляются две команды “Peer
Choose” (“Выбрать режим”) и ”Exit” (”Выход”) и обработчик событий. Команда
“Peer Choose” запускает мидлет в выбранном режиме работы, а команда ”Exit”
завершает работу приложения. В результате выполнения команды “Peer Choose”
создается объект NCCanvas – основной холст мидлета и запускается поток холста. В
качестве дополнительных параметров конструктору класса NCCanvas передаются
ссылки на мидлет и дисплей для возможности обращения к мидлету и дисплею из
класса NCCanvas, а также выбранный режим работы для задания режима
фукционирования холста. Форма выводится на экран дисплея.
Листинг 1. Программа сетевого соединения с использованием протокола
TCP/IP
/* class NetCalcSocketMIDlet */
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class NetCalcSocketMIDlet extends MIDlet implements CommandListener {
private Form
form;
private Display d;
private ChoiceGroup choices;
private NCCanvas ncCanvas;
private Command exit = new Command( "Exit", Command.EXIT, 0 );
private Command peer = new Command( "Peer Choose", Command.OK, 1 );
public NetCalcSocketMIDlet() {
// Create form
form = new Form("Net calc");
// Add the peer choice group
String[] peerNames = { "Server", "Client" };
choices = new ChoiceGroup("Please select peer type:", Choice.EXCLUSIVE,
peerNames, null);
form.append(choices);
// Add the exit and peer commands
form.addCommand(exit);
form.addCommand(peer);
form.setCommandListener(this);
// Set the form as the current screen
d = Display.getDisplay( this );
d.setCurrent( form );
}
public void startApp() {}
public void pauseApp() {}
public void destroyApp(boolean unconditional) {
ncCanvas.stop(); }
public void commandAction( Command c, Displayable s) {
if( c == exit ) {
destroyApp(true);
notifyDestroyed();
}
if( c == peer ) {
// Find out which type of peer connection is being used
String name = choices.getString(choices.getSelectedIndex());
// Create the game canvas
if (ncCanvas == null) { ncCanvas = new NCCanvas( this, d, name ); }
// Start up the ncCanvas
ncCanvas.start();
}
}
}
/* class NCCanvas */
import javax.microedition.lcdui.*;
import javax.microedition.lcdui.game.*;
import java.io.*;
import javax.microedition.io.*;
public class NCCanvas extends GameCanvas implements Runnable, CommandListener {
private Display d;
private NetCalcSocketMIDlet parent;
private NCClient client;
private NCServer server;
private Form form_num;
private TextField first_arg, second_arg;
private Image ImageServer, ImageClient;
private boolean isClient;
private boolean sleeping, full_flag;
private String status, arguments;
private Command numbers = new Command( "Numbers", Command.SCREEN, 1 );
private Command close = new Command( "Close", Command.SCREEN, 1 );
private Command exit = new Command( "Exit", Command.EXIT, 0 );
private Command enter = new Command( "Enter", Command.OK, 1 );
private Command cancel = new Command( "Cancel", Command.CANCEL, 1 );
public void removeNumbersCommand() {
removeCommand( numbers ); }
public NCCanvas( NetCalcSocketMIDlet m, Display display, String peerType ) {
super(true);
parent = m;
d = display;
// Remember the peer type
isClient = peerType.equals("Client");
}
public void start() {
// Set the canvas as the current screen
d.setCurrent(this);
// Load background images
try {
ImageServer = Image.createImage( "/Server.png" );
ImageClient = Image.createImage( "/Client.png" );
}
catch( IOException e ) { System.err.println("Loading images error"); }
// Start the networking service
if( isClient ) {
client = new NCClient(this);
client.start();
addCommand( numbers );
}
else {
server = new NCServer(this);
server.start();
}
addCommand( close );
addCommand( exit );
setCommandListener(this);
full_flag = false;
// Start the thread
sleeping = false;
Thread t = new Thread(this);
t.start();
}
public void stop() {
// Stop the thread
sleeping = true;
}
public void run() {
Graphics g = getGraphics();
// The main game loop
while( !sleeping ) {
update();
draw(g);
try {
Thread.sleep( 1500 );
}
catch( InterruptedException ie ) {}
}
}
private void update() {
// Process user input to issue operation
int keyState = getKeyStates();
if( isClient && full_flag )
{
if( ( keyState & LEFT_PRESSED ) != 0 ) {
if( isClient ) client.sendMessage1( arguments.concat("+") );
status = "Addition";
}
if( ( keyState & RIGHT_PRESSED ) != 0 ) {
if( isClient ) client.sendMessage1( arguments.concat("-") );
status = "Substruct";
}
if( ( keyState & UP_PRESSED ) != 0 ) {
if( isClient ) client.sendMessage1( arguments.concat("*") );
status = "Multiplication";
}
if( ( keyState & DOWN_PRESSED ) != 0 ) {
if( isClient ) client.sendMessage1( arguments.concat("/") );
status = "Division";
}
}
}
private void draw(Graphics g) {
int x = g.getClipWidth();
int y = g.getClipHeight();
g.setColor( 0xFFFFFF );
g.fillRect( 0, 0, x, y );
g.setColor( 0, 0, 0 ); // black
if( isClient )
{
g.drawImage( ImageClient, 0, 0, Graphics.TOP | Graphics.LEFT );
g.drawString( "Client", getWidth() / 2, 15, Graphics.TOP | Graphics.HCENTER);
}
else
{
g.drawImage( ImageServer, 0, 0, Graphics.TOP | Graphics.LEFT );
g.drawString( "Server", getWidth() / 2, 15, Graphics.TOP | Graphics.HCENTER);
}
// Draw the status message
if( status != null )
g.drawString(status, getWidth() / 2, 225, Graphics.TOP | Graphics.HCENTER);
// Flush the offscreen graphics buffer
flushGraphics();
}
public void setStatus( String s ) {
// Set the status message
status = s;
}
public String getStatus() { return status; }
public void commandAction( Command c, Displayable s) {
if( c == exit ) {
parent.destroyApp(true);
parent.notifyDestroyed();
}
if( c == close )
{
if( isClient )
{
client.stop();
removeCommand( numbers );
}
else
{
server.stop();
}
removeCommand( close );
}
if( c == numbers )
{
form_num = new Form( "Numbers" );
form_num.addCommand(enter);
form_num.addCommand(cancel);
form_num.setCommandListener(this);
// first argument field
first_arg = new TextField( "First argument", "", 5, TextField.NUMERIC) ;
form_num.append( first_arg );
// second argument field
second_arg = new TextField( "Second argument", "", 5, TextField.NUMERIC) ;
form_num.append( second_arg );
d.setCurrent( form_num );
full_flag = false;
}
if( c == enter )
{
if( ( first_arg.size() == 0 ) || ( second_arg.size() == 0 ) )
{
Alert al = new Alert( "Warning", "Missing argument", null,
AlertType.WARNING );
al.setTimeout( 2000 );
d.setCurrent( al );
}
else if( ( Integer.parseInt( first_arg.getString()) > 9999 ) ||
( Integer.parseInt( second_arg.getString()) > 9999 ) )
{
Alert al = new Alert( "Warning", "Illegal argument", null,
AlertType.WARNING );
al.setTimeout( 2000 );
d.setCurrent( al );
}
else
{
arguments = formString( first_arg, second_arg );
full_flag = true;
setStatus( "Numbers: " + first_arg.getString() + " , " + second_arg.getString() );
setCommandListener(this);
d.setCurrent( this );
}
}
if( c == cancel )
{
setCommandListener(this);
d.setCurrent( this );
if( arguments != null ) full_flag = true;
else
{
full_flag = false;
setStatus( "Connected to peer server." );
}
}
}
private String formString( TextField tf1, TextField tf2 )
{
int i = 0;
StringBuffer first_str = new StringBuffer(5);
StringBuffer second_str = new StringBuffer(5);
first_str.append(tf1.getString());
second_str.append(tf2.getString());
int length_first_str = tf1.getString().length();
int length_second_str = tf2.getString().length();
String res;
boolean minus1 = false;
boolean minus2 = false;
if( first_str.charAt(0) == '-' )
{
minus1 = true;
first_str.setCharAt(0,'0');
}
if( second_str.charAt(0) == '-' )
{
minus2 = true;
second_str.setCharAt(0,'0');
}
while( length_first_str++ != 5 )
first_str.insert( i++, '0');
if( minus1 ) first_str.setCharAt(0,'-');
i = 0;
while( length_second_str++ != 5 )
second_str.insert( i++, '0');
if( minus2 ) second_str.setCharAt(0,'-');
res = first_str.toString() + second_str.toString();
return res;
}
public String receiveMessage( InputStream is ) {
String msg;
StringBuffer sbuf = new StringBuffer();
int c = 0;
try
{
while( (c = is.read())!='\n')
sbuf.append( (char) c );
}
catch( IOException ioe ) { }
msg = new String(sbuf);
return msg;
}
}
/* class NCServer */
import javax.microedition.io.*;
import java.io.*;
public class NCServer implements Runnable {
private NCCanvas
canvas;
private InputStream
is;
private OutputStream
os;
private SocketConnection sc;
private ServerSocketConnection scn;
private boolean closing_rcv, closing_snd;
public NCServer( NCCanvas c ) { canvas = c; }
public void start() {
Thread t = new Thread(this);
t.start();
closing_rcv = false;
closing_snd = false;
}
public void stop() {
sendMessage( os, "stop" );
closing_snd = true;
}
public void run() {
try {
// Connect to the peer client
canvas.setStatus("Waiting for peer client...");
scn = ( ServerSocketConnection ) Connector.open("socket://:5000");
// Wait for a connection
sc = ( SocketConnection ) scn.acceptAndOpen();
canvas.setStatus( "Connected to peer client." );
is = sc.openInputStream();
os = sc.openOutputStream();
while (true) {
String msg = canvas.receiveMessage( is );
if( msg.equals("stop"))
{
canvas.setStatus( "Connection closed." );
closing_rcv = true;
break;
}
else
{
sendMessage( os, decodeAndOperation( msg ) );
}
} // end while
close();
}
catch( IOException ioe ) {
System.err.println( "The network port is already taken." );
}
catch( Exception e ) { }
}
public void sendMessage( OutputStream os, String msg ) {
// Send the message
try {
// Convert the string message to bytes
os.write( msg.getBytes() );
os.write( "\n".getBytes() );
}
catch( Exception e ) { }
}
public String decodeAndOperation( String msg ) {
// Parse message
String str_first_arg = msg.substring( 0, 5 );
String str_second_arg = msg.substring( 5, 10 );
String str_res;
int first_arg = Integer.parseInt( str_first_arg );
int second_arg = Integer.parseInt( str_second_arg );
int res = 0;
// Run operation
switch( msg.charAt(10) ) {
case '+': res = first_arg + second_arg; break;
case '-': res = first_arg - second_arg; break;
case '*': res = first_arg * second_arg; break;
case '/': res = first_arg / second_arg; break;
} // end switch
str_res = ( new Integer( res ) ).toString();
canvas.setStatus( ( new Integer( first_arg ) ).toString() + " "
+ msg.charAt(10) + " " + ( new Integer( second_arg ) ).toString() + " = " + str_res );
return str_res;
}
public void close() {
try {
// Disconnect to the peer client
if( closing_rcv && closing_snd )
{
if( is != null ) is.close();
if( os != null ) os.close();
if( sc != null ) sc.close();
if( scn != null ) scn.close();
}
}
catch( IOException ioe ) { }
}
}
/* class NCClient */
import javax.microedition.io.*;
import java.io.*;
public class NCClient implements Runnable {
private NCCanvas
canvas;
private InputStream
is;
private OutputStream
os;
private SocketConnection sc;
private boolean closing_rcv, closing_snd;
public NCClient( NCCanvas c ) { canvas = c; }
public void start() {
Thread t = new Thread(this);
t.start();
closing_rcv = false;
closing_snd = false;
}
public void stop() {
sendMessage( os, "stop" );
closing_snd = true;
}
public void run() {
try {
// Connect to the peer server
sc = (SocketConnection) Connector.open( "socket://localhost:5000" );
canvas.setStatus( "Connected to peer server." );
is = sc.openInputStream();
os = sc.openOutputStream();
while(true) {
String msg = canvas.receiveMessage( is );
if( msg.equals("stop"))
{
closing_rcv = true;
canvas.removeNumbersCommand();
canvas.setStatus( "Connection closed." );
break;
}
else
canvas.setStatus( "result of " + canvas.getStatus() + " equal " + msg );
}
close();
}
catch( ConnectionNotFoundException cnfe ) {
System.err.println( "The network server is unavailable." );
}
catch( IOException ioe ) { }
}
public void sendMessage1( String msg ) {
sendMessage( os, msg );
}
public void sendMessage( OutputStream os, String msg ) {
// Send the message
try {
// Convert the string message to bytes
os.write( msg.getBytes() );
os.write( "\n".getBytes() );
}
catch( Exception e ) { }
}
public void close() {
try {
// Disconnect to the peer server
if( closing_rcv && closing_snd )
{
if( is != null ) is.close();
if( os != null ) os.close();
if( sc != null ) sc.close();
}
}
catch( IOException ioe ) { }
}
}
Тестирование мобильного приложения
1. Запускаем мобильное устройство У1 в режиме сервера (рис.3)
Рис.3 – Экранная форма выбора режима функционирования устройства У1
2. Сервер ожидает запрос на соединение от клиента (рис.4)
Рис.4 – Экранная форма устройства У1 в режиме ожидания запроса клиента
3. Запускаем мобильное устройство У2 в режиме клиента (рис.5)
Рис.5 – Экранная форма выбора режима функционирования устройства У2
4. Соединение между сервером и клиентом установлено (рис.6)
Рис.6 – Экранные формы устройств после установки соединения
5. В меню клиента выбираем команду ”Numbers” для перехода в форму ввода
значений операндов (рис.7)
Рис.7 – Экранная форма устройства У2 (команда ”Numbers”)
6. Вводим значения операндов и выбираем команду ”Enter” (рис.8)
Рис.8 – Экранная форма устройства У2 для ввода значений аргументов
7. Введенные числа отображаются в строке состояния клиента (рис.9)
Рис.9 – Экранная форма устройства У2 после ввода значений аргументов
8. Выбираем команду деления. Содержимое дисплеев устройств после выполнения
команды деления представлено на рис.10.
Рис.10 – Экранные формы устройств после выполнения операции деления
9. Сервер выбирает команду ”Close” и завершает соединение. Информация о
завершении соединения отображается в строке состояния клиента (рис.11). Сервер
ждет ответное сообщение о завершении соединения от клиента.
Рис.11 – Экранная форма устройства У2 после завершения соединения
10. Клиент выполняет команду ”Close” и завершает соединение. Информация
отображается в строке состояния сервера (рис.12).
Рис.12 – Экранная форма устройства У1 после завершения соединения
Дейтаграммное соединение
Программа сетевого соединения с использованием протокола UDP приведена
в листинге 2. Приложение, как и в случае сокетного соединения, состоит из четырех
классов. Класс NetCalcDatagramMIDlet – класс мидлета, класс NCCanvas – класс
холста
устройств,
класс
NCClient
–
класс
функционирования
устройства
дейтаграммного клиента, NCServer – класс устройства дейтаграммного сервера.
Классы NetCalcDatagramMIDlet и NCCanvas практически совпадают с подобными
классами для сокетного соединения. В классах NCClient и NCServer отличия
содержатся в методах run() этих классов в связи с организацией дейтаграммного
соединения вместо сокетного.
Метод run() класса NCServer дейтаграммного соединения
В методе run() выполняются следующие действия:
 в строку состояния холста выводится сообщение ”Waiting for peer client…” об
ожидании подключения клиента;
 сервер
открывает
дейтаграммное
Connector.open(“datagram://:5555”)
DatagramConnection.
возвращая
соединение,
объект
вызывая
dc
метод
интерфейса
Номер используемого порта – произвольный (в примере,
5555), но важно, чтобы сервер и клиент использовали один и тот же порт для
соединения;
 запускает бесконечный цикл, в котором выполняются попытки принятия
дейтаграммных пакетов клиента;
 создает объект дейтаграммы размером 32 байта (предполагается, что
максимальный размер сообщения не превышает 32 байта), принимает дейтаграмму
клиента и запоминает адрес, чтобы послать ответную дейтаграмму серверу;
 получает дейтаграммное сообщение ”Client” от клиента об успешном
установлении соединения, выводит в строку состояния сообщение ”Connected to
peer client.” и отправляет клиенту ответное сообщение ”Server” об установлении
соединения;
 получает дейтаграммы со значениями операндов и типом операции от
клиента, выполняет операцию и посылает дейтаграммы с результатами клиенту,
отображая информацию в строке состояния холста мидлета;
 цикл работает до появления сообщения “stop” от клиента;
 выводит сообщение "Connection closed." в строку состояния.
Метод run() класса NCClient дейтаграммного соединения
В методе run() выполняются следующие действия:
 в строку состояния холста выводится сообщение ”Connecting to peer server…”,
что говорит о том, что клиент пытается соединиться с сервером;
 сервер
открывает
дейтаграммное
соединение,
вызывая
метод
Connector.open( "datagram://localhost:5555" ) возвращая объект dc интерфейса
DatagramConnection.
Номер используемого порта (в примере, 5555) должен
совпадать с портом соединения сервера;
 запускает бесконечный цикл, в котором выполняются попытки принятия
дейтаграммных пакетов сервера;
 если соединение не установлено отправляет сообщение ”Client” серверу;
 создает объект дейтаграммы размером 32 байта и принимает дейтаграмму
сервера;
 при получении сообщения ”Server” от сервера в строку состояния холста
мидлета выводится сообщение ”Connected to peer server.”;
 посылает дейтаграммы со значениями операндов и типом операции и получает
дейтаграммы с результатами от сервера, отображая информацию в строке состояния
холста мидлета;
 цикл работает до появления сообщения “stop” от сервера;
 выводит сообщение "Connection closed." в строку состояния.
Отметим также, три основных отличия в кодах сервера и клиента. Первое при
открытии дейтаграммного соединения клиентом в строке URL используется поле
хоста (в примере, localhost).
В соединении сервера поле хоста не указывается.
Второе отличие в методе sendMessage(String) – клиент не использует адрес при
отправлении пакетов серверу, а сервер использует адрес клиента, который
извлекает из принятой дейтаграммы клиента.
NCClient
есть
булевская
переменная
он
Третье отличие в коде класса
connected,
котрая
после
успешного
установления соединения устанавливается в true.
Листинг 2. Программа сетевого соединения с использованием протокола UDP
/* class NetCalcDatagramMIDlet */
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class NetCalcDatagramMIDlet extends MIDlet implements CommandListener {
private Form
form;
private Display d;
private ChoiceGroup choices;
private NCCanvas ncCanvas;
private Command exit = new Command( "Exit", Command.EXIT, 0 );
private Command peer = new Command( "Peer Choose", Command.OK, 1 );
public NetCalcDatagramMIDlet() {
// Create form
form = new Form("Net calc");
// Add the peer choice group
String[] peerNames = { "Server", "Client" };
choices = new ChoiceGroup("Please select peer type:", Choice.EXCLUSIVE, peerNames, null);
form.append(choices);
// Add the exit and peer commands
form.addCommand(exit);
form.addCommand(peer);
form.setCommandListener(this);
// Set the form as the current screen
d = Display.getDisplay( this );
d.setCurrent( form );
}
public void startApp() {}
public void pauseApp() {}
public void destroyApp(boolean unconditional) {
ncCanvas.stop();
}
public void commandAction( Command c, Displayable s) {
if( c == exit ) {
destroyApp(true);
notifyDestroyed();
}
if( c == peer ) {
// Find out which type of peer connection is being used
String name = choices.getString(choices.getSelectedIndex());
// Create the game canvas
if (ncCanvas == null) {
ncCanvas = new NCCanvas( this, d, name );
}
// Start up the ncCanvas
ncCanvas.start();
}
}
}
/* class NCCanvas */
import javax.microedition.lcdui.*;
import javax.microedition.lcdui.game.*;
import java.io.*;
import javax.microedition.io.*;
public class NCCanvas extends GameCanvas implements Runnable, CommandListener {
private Display d;
private NetCalcMIDlet parent;
private NCClient client;
private NCServer server;
private Form form_num;
private TextField first_arg, second_arg;
private Image ImageServer, ImageClient;
private boolean isClient;
private boolean sleeping, full_flag;
private String status, arguments;
private Command numbers = new Command( "Numbers", Command.SCREEN, 1 );
private Command close = new Command( "Close", Command.SCREEN, 1 );
private Command exit = new Command( "Exit", Command.EXIT, 0 );
private Command enter = new Command( "Enter", Command.OK, 1 );
private Command cancel = new Command( "Cancel", Command.CANCEL, 1 );
public NCCanvas( NetCalcMIDlet m, Display display, String peerType ) {
super(true);
parent = m;
d = display;
// Remember the peer type
isClient = peerType.equals("Client");
}
public void removeNumbersCommand()
{
removeCommand( numbers );
}
public void start() {
// Set the canvas as the current screen
d.setCurrent(this);
// Load background images
try {
ImageServer = Image.createImage( "/Server.png" );
ImageClient = Image.createImage( "/Client.png" );
}
catch( IOException e ) { System.err.println("Loading images error"); }
// Start the networking service
if( isClient ) {
client = new NCClient(this);
client.start();
addCommand( numbers );
}
else {
server = new NCServer(this);
server.start();
}
addCommand( close );
addCommand( exit );
setCommandListener(this);
// Start the thread
sleeping = false;
Thread t = new Thread(this);
t.start();
}
public void stop() {
// Stop the thread
sleeping = true;
}
public void run() {
Graphics g = getGraphics();
// The main game loop
while( !sleeping ) {
update();
draw(g);
try {
Thread.sleep( 1500 );
}
catch( InterruptedException ie ) {}
}
}
private void update() {
// Process user input to issue operation
int keyState = getKeyStates();
if( isClient && full_flag )
{
if( ( keyState & LEFT_PRESSED ) != 0 ) {
if( isClient ) client.sendMessage( arguments.concat("+") );
status = "Addition";
}
if( ( keyState & RIGHT_PRESSED ) != 0 ) {
if( isClient ) client.sendMessage( arguments.concat("-") );
status = "Substruct";
}
if( ( keyState & UP_PRESSED ) != 0 ) {
if( isClient ) client.sendMessage( arguments.concat("*") );
status = "Multiplication";
}
if( ( keyState & DOWN_PRESSED ) != 0 ) {
if( isClient ) client.sendMessage( arguments.concat("/") );
status = "Division";
}
}
}
private void draw(Graphics g) {
int x = g.getClipWidth();
int y = g.getClipHeight();
g.setColor( 0xFFFFFF );
g.fillRect( 0, 0, x, y );
g.setColor( 0, 0, 0 ); // black
if( isClient )
{
g.drawImage( ImageClient, 0, 0, Graphics.TOP | Graphics.LEFT );
g.drawString( "Client", getWidth() / 2, 15, Graphics.TOP | Graphics.HCENTER);
}
else
{
g.drawImage( ImageServer, 0, 0, Graphics.TOP | Graphics.LEFT );
g.drawString( "Server", getWidth() / 2, 15, Graphics.TOP | Graphics.HCENTER);
}
// Draw the status message
g.drawString(status, getWidth() / 2, 225, Graphics.TOP | Graphics.HCENTER);
// Flush the offscreen graphics buffer
flushGraphics();
}
public void setStatus( String s ) {
// Set the status message
status = s;
}
public String getStatus() { return status; }
public void commandAction( Command c, Displayable s) {
if( c == exit ) {
parent.destroyApp(true);
parent.notifyDestroyed();
}
if( c == close ) {
if( isClient )
{
client.stop();
removeCommand( numbers );
}
else server.stop();
removeCommand( close );
}
if( c == numbers )
{
form_num = new Form( "Numbers" );
form_num.addCommand(enter);
form_num.addCommand(cancel);
form_num.setCommandListener(this);
// first argument field
first_arg = new TextField( "First argument", "", 5, TextField.NUMERIC) ;
form_num.append( first_arg );
// second argument field
second_arg = new TextField( "Second argument", "", 5, TextField.NUMERIC) ;
form_num.append( second_arg );
d.setCurrent( form_num );
full_flag = false;
}
if( c == enter )
{
if( ( first_arg.size() == 0 ) || ( second_arg.size() == 0 ) )
{
Alert al = new Alert( "Warning", "Missing argument", null,
AlertType.WARNING );
al.setTimeout( 2000 );
d.setCurrent( al );
}
else if( ( Integer.parseInt( first_arg.getString()) > 9999 ) ||
( Integer.parseInt( second_arg.getString()) > 9999 ) )
{
Alert al = new Alert( "Warning", "Illegal argument", null,
AlertType.WARNING );
al.setTimeout( 2000 );
d.setCurrent( al );
}
else
{
arguments = formString( first_arg, second_arg );
full_flag = true;
setStatus( "Numbers: " + first_arg.getString() + " , "
+ second_arg.getString() );
setCommandListener(this);
d.setCurrent( this );
}
}
if( c == cancel )
{
setCommandListener(this);
d.setCurrent( this );
if( arguments != null ) full_flag = true;
else
{
full_flag = false;
setStatus("Connected to peer server. ");
}
}
}
private String formString( TextField tf1, TextField tf2 )
{
int i = 0;
StringBuffer first_str = new StringBuffer(5);
StringBuffer second_str = new StringBuffer(5);
first_str.append(tf1.getString());
second_str.append(tf2.getString());
int length_first_str = tf1.getString().length();
int length_second_str = tf2.getString().length();
String res;
boolean minus1 = false;
boolean minus2 = false;
if( first_str.charAt(0) == '-' )
{
minus1 = true;
first_str.setCharAt(0,'0');
}
if( second_str.charAt(0) == '-' )
{
minus2 = true;
second_str.setCharAt(0,'0');
}
while( length_first_str++ != 5 )
first_str.insert( i++, '0');
if( minus1 ) first_str.setCharAt(0,'-');
i = 0;
while( length_second_str++ != 5 )
second_str.insert( i++, '0');
if( minus2 ) second_str.setCharAt(0,'-');
res = first_str.toString() + second_str.toString();
return res;
}
}
/* class NCServer */
import javax.microedition.io.*;
import java.io.*;
public class NCServer implements Runnable {
private NCCanvas
canvas;
private DatagramConnection dc;
private String
address;
private boolean closing_rcv, closing_snd;
public NCServer( NCCanvas c ) {
canvas = c;
}
public void start() {
Thread t = new Thread(this);
t.start();
closing_rcv = false;
closing_snd = false;
}
public void stop() {
sendMessage( "stop" );
closing_snd = true;
}
public void run() {
try {
// Connect to the peer client
canvas.setStatus("Waiting for peer client...");
dc = null;
while (dc == null)
dc = ( DatagramConnection ) Connector.open("datagram://:5555");
while (true) {
// Try to receive a datagram packet
Datagram dg = dc.newDatagram(32);
dc.receive(dg);
address = dg.getAddress();
// Make sure the datagram actually contains data
if( dg.getLength() > 0 ) {
String data = new String( dg.getData(), 0, dg.getLength() );
if( data.equals( "Client" ) ) {
// Notify the user of a successful network connection
canvas.setStatus( "Connected to peer client." );
// Try to reply with a connection message
sendMessage( "Server" );
}
else if( data.equals( "stop" ) ) {
canvas.setStatus( "Connection closed." );
closing_rcv = true;
break;
}
else {
// Send along the network data
sendMessage( decodeAndOperation( data ) );
}
}
}
}
catch( IOException ioe ) { System.err.println( "The network port is already taken." ); }
catch( Exception e ) { }
}
public void sendMessage( String message ) {
// Send the message
try {
// Convert the string message to bytes
byte[] bytes = message.getBytes();
// Send the message
Datagram dg = null;
dg = dc.newDatagram( bytes, bytes.length, address );
dc.send( dg );
}
catch( Exception e ) { }
}
public String decodeAndOperation( String msg ) {
// Parse message
String str_first_arg = msg.substring( 0, 5 );
String str_second_arg = msg.substring( 5, 10 );
String str_res;
int first_arg = Integer.parseInt( str_first_arg );
int second_arg = Integer.parseInt( str_second_arg );
int res = 0;
// Run operation
switch( msg.charAt(10) ) {
case '+': res = first_arg + second_arg; break;
case '-': res = first_arg - second_arg; break;
case '*': res = first_arg * second_arg; break;
case '/': res = first_arg / second_arg;
break;
} // end switch
str_res = ( new Integer( res ) ).toString();
canvas.setStatus( ( new Integer( first_arg ) ).toString() + " "
+ msg.charAt(10) + " " + ( new Integer( second_arg ) ).toString() + " = " + str_res );
return str_res;
}
public void close() {
try {
// Disconnect to the peer client
if( closing_rcv && closing_snd )
{
if( dc != null ) dc.close();
}
}
catch( IOException ioe ) { System.err.println( "The network port doesn't exist." ); }
}
}
/* class NCClient */
import javax.microedition.io.*;
import java.io.*;
public class NCClient implements Runnable {
private NCCanvas
canvas;
private DatagramConnection dc;
private boolean
connected;
private boolean closing_rcv, closing_snd;
public NCClient( NCCanvas c ) {
canvas = c;
connected = false;
}
public void start() {
Thread t = new Thread(this);
t.start();
closing_rcv = false;
closing_snd = false;
}
public void stop() {
sendMessage( "stop" );
closing_snd = true;
}
public void run() {
try {
// Connect to the peer server
canvas.setStatus( "Connecting to peer server..." );
dc = null;
while( dc == null )
dc = (DatagramConnection) Connector.open( "datagram://localhost:5555" );
while (true) {
// Try to send a connection message
if( !connected )
sendMessage( "Client" );
// Try to receive a datagram packet
Datagram dg = dc.newDatagram( 32 );
dc.receive(dg);
// Make sure the datagram actually contains data
if( dg.getLength() > 0 ) {
String data = new String( dg.getData(), 0, dg.getLength() );
if( data.equals( "Server" ) ) {
// Notify the user of a successful network connection
canvas.setStatus("Connected to peer server.");
connected = true;
}
else if( data.equals( "stop" ) ) {
canvas.setStatus( "Connection closed." );
canvas.removeNumbersCommand();
closing_rcv = true;
break;
}
else {
// Send along the network data
receiveMessage( data );
}
}
}
}
catch( ConnectionNotFoundException cnfe ) {
System.err.println( "The network server is unavailable." );
}
catch( IOException ioe ) { }
}
public void sendMessage( String msg ) {
// Send the message
try {
// Convert the string message to bytes
byte[] bytes = msg.getBytes();
// Send the message
Datagram dg = null;
dg = dc.newDatagram( bytes, bytes.length );
dc.send( dg );
}
catch( Exception e ) { }
}
public void receiveMessage( String msg ) {
canvas.setStatus( "result of " + canvas.getStatus() + " equal " + msg );
}
public void close() {
try {
// Disconnect to the peer server
if( closing_rcv && closing_snd )
{
if( dc != null ) dc.close();
}
}
catch( IOException ioe ) { System.err.println( "The network port doesn't exist." ); }
}
}
Тестирование мобильного
приложения
для дейтаграммного соединения
аналогично тестированию мобильного прилодения с использованием протокола
TCP/IP.
Download