Рабочий каталог расположен в
Итак, мы собираемся написать наше первое распределенное приложение. Способ, с помощью которого мы собираемся это сделать, - самый простой. Раз нам нужно передавать данные между компонентами нашего приложения по сети, следовательно, этому нужно научиться. Первый способ, который мы используем для решения задачи, будет основываться на входящем в состав пакета java.net API. Этот пакет предоставляет возможность для реализации сетевого взаимодействия с применением одного из двух транспортных протоколов: UDP и TCP.
Архитектура протоколов TCP/IP, известная как набор протоколов TCP/IP, возникла в результате исследований в области протоколов и разработок, выполнявшейся в экспериментальной , которая была основана Управлением перспективных исследовательских программ Министерства обороны США ( )[3.26]. Этот набор протоколов состоит из большого собрания протоколов, изданных Координационным советом по сети Internet ( в Internet.
В общем можно сказать, что в обмене информацией всегда принимают участие три агента: приложения, узлы (компьютеры) и сети. К приложениям относятся программы, предназначенные, например, для передачи файлов или электронной почты. Приложения, о которых идет речь, являются распределенными приложениями, включающими в себя обмен данными между двумя и более компьютерными системами. Компьютеры соединены между собой в сети, и предназначенные для обмена данные передаются по сети от одного компьютера к другому. Таким образом, передача данных от одного приложения другому (или от одного компонента другому в рамках одного распределенного приложения) включает в себя, во-первых, получение данных тем узлом, на котором находится приложение-адресат, и, во-вторых, получение данных именно тем выполняющимся на узле-адресате приложением, которому они предназначены (одновременно на узле могут выполняться несколько приложений).
Ввиду этого в задаче обмена информацией выделяют пять основных относительно независимых уровней:
На физическом уровне находится физический интерфейс между устройством передачи данных и передающей средой. На этом уровне задаются характеристики передающей среды, природа сигналов, скорость передачи данных и т.д.
Уровень доступа к сети связан с обменом данными между конечной системой (рабочей станцией, сервером и т.д.) и сетью, к которой подсоединена эта система. Узел-отправитель должен передать в сеть адрес узла-адресата, чтобы сеть могла направить данные по месту требования.
Уровень доступа к сети рассматривается в связи с доступом к сети и маршрутизацией данных между двумя подключенными к сети оконечными системами. В тех случаях, когда устройства подключены к разным сетям, нужны дополнительные процедуры, позволяющие данным переходить из одной сети в другую. Такие функции относятся к межсетевому уровню. На этом уровне функции межсетевой маршрутизации предоставляются с помощью Internet -протокола ( IP ). Internet -протокол может быть реализован не только в конечных системах, но и в маршрутизаторах.
Независимо от природы приложений обмен данными должен быть надежным, т.е. хотелось бы иметь уверенность в том, что все данные попали к приложению-адресату и что эти данные получены в том порядке, в котором они были отправлены. Механизмы обеспечения надежности, по сути, независимы от природы приложений, таким образом, имеет смысл выделить такие механизмы в один общий уровень, совместно используемый всеми приложениями. Этот уровень называется транспортным. Чаще всего для него применяется ).
Наконец, в уровень приложений заложена логика, необходимая для обеспечения работы различных пользовательских приложений. Приложению каждого вида нужен отдельный модуль, специально предназначенный для этого приложения. Именно разработкой уровня приложения мы в дальнейшем и займемся.
Итак, чтобы обмен информацией был возможен, каждый элемент системы должен иметь уникальный адрес. Фактически, нужно указать два уровня адресации. Каждый TCP ) доставить данные нужному процессу. Этот адрес - адрес процесса - в терминологии протоколов семейства TCP/IP называют портом.
Для большинства приложений, выполняющихся в рамках архитектуры протокола TCP/IP, протоколом транспортного уровня выступает TCP. Этот протокол обеспечивает надежное соединение для передачи данных от одного приложения к другому. Кроме него существует еще один широко используемый протокол транспортного уровня, входящий в набор протоколов TCP/IP: пользовательский протокол датаграмм ( User ).
Протокол UDP предоставляет сервис без установления соединения, предназначенный для процедур на уровне приложения; он не гарантирует доставку, сохранение последовательности при приеме переданных пакетов или защиту от их дублирования. Он позволяет процессу отправлять сообщения другим процессам, с помощью минимального протокольного механизма. Протокол UDP выполняет крайне ограниченный набор функций, так как работает без установления соединения. По сути, он добавляет к протоколу IP некоторые возможности адресации портов.
Рассмотрим вкратце возможности, которые предоставляет нам java и ее пакет java.net.
Начнем мы с API, использующего протокол UDP. UDP является протоколом, применяющим для передачи датаграммы. Как уже говорилось, этот протокол не является надежным, поскольку сообщения, передаваемые с его помощью, могут теряться или приходить в другой последовательности, отличной от последовательности их отправки. Таким образом, для обеспечения надежной передачи необходимо организовывать надстройку над этим протоколом, обеспечивающую, например, нумерацию пакетов,
повторную передачу пакетов при истечении времени ожидания и т.д. Длина одного сообщения (одной датаграммы) при использовании этого протокола ограничена 65536 байтами (причем многие реализации вообще ограничивают размер датаграммы 8К), в случае необходимости пересылки порции данных большего размера они должны быть разбиты на куски отправителем и снова собраны получателем. Передача сообщения - не блокирующая, прием - блокирующий с возможностью прерывания по истечении времени ожидания.
Для работы с UDP в пакете java.net определены следующие классы.
IP -адрес узла и порт). Класс предназначен для представления единичной дата-граммы (сообщения). Этот класс используется как для создания сообщения с целью последующей его отправки, так и при приеме сообщения (функция приема возвращает экземпляр этого класса).UDP -дата-грамм. Один из конструкторов принимает в качестве аргумента порт, с которым связывается сокет, другой конструктор, без аргументов, задействует в качестве порта первый попавшийся свободный порт. Класс имеет методы send и receive, для, соответственно, передачи и приема датаграмм. Метод setSoTimeout устанавливает тайм-аут для операций сокета.Ниже приведены две простые программы, использующие рассмотренные механизмы для организации взаимодействия.
UDPClient
Первая программа создает сокет, соединяется с сервером (порт 6789), пересылает ему сообщение и ждет ответа (пример 3.1).
1 import java.net.*;
2 import java.io.*;
3 public class UDPClient{
4 public static void main(String args[]){
5 // аргументы - сообщение и адрес сервера
6 try {
7 DatagramSocket aSocket = new DatagramSocket(); // create socket
8 byte [] message = args[0].getBytes();
9 InetAddress aHost = InetAddress.getByName(args[1]); // DNS lookup
10 int serverPort = 6789;
11 DatagramPacket request =
12 new DatagramPacket(message, args[0].length(), aHost, serverPort);
13 aSocket.send(request);
14 //send message
15 byte[] buffer = new byte[1000];
16 DatagramPacket reply = new DatagramPacket(buffer, buffer.length);
17 aSocket.receive(reply);
18 //wait for reply
19 System.out.println("Reply: " + new String(reply.getData()));
20 aSocket.close();
21 } catch (SocketException e){ System.out.println("Socket: " + e.getMessage());
22 // socket creation failed
23 } catch (IOException e){ System.out.println("IO: " + e.getMessage());
24 // ошибка при приеме
25 }
26 }
27 }
Поскольку это первые наши программы, прокомментируем их более подробно.
В первых строках (строки 1,2) происходит импорт необходимых библиотек (пакетов) классов - в нашем случае это java.net и java.io. Первый пакет содержит необходимые нам классы для работы с UDP, второй определяет необходимые классы ввода/вывода.
Наш класс (строка 3) называется UDPClient,он содержит единственный статический метод main (строка 4), являющийся точкой
UDPServer
Другая программа (сервер) создает сокет и обслуживает запросы клиента (пример 3.2).
1 import java.net.*;
2 import java.io.*;
3 public class UDPServer{
4 public static void main(String args[]) {
5 DatagramSocket aSocket = null;
6 try{
7 aSocket = new DatagramSocket(6789); // create socket at port
8 byte[] buffer = new byte[1000];
9 while(true){
10 DatagramPacket request = new DatagramPacket(buffer, buffer.length);
11 aSocket.receive(request);
12 DatagramPacket reply =
13 new DatagramPacket(request.getData(), request.getLength(),
14 request.getAddress(), request.getPort());
15 aSocket.send(reply);
16 }
17 } catch (SocketException e) {System.out.println("Socket: " + e.getMessage());
18 // socket creation failed
19 } catch (IOException e) {System.out.println("IO: " + e.getMessage());
20 } finally {if(aSocket != null) aSocket.close();}
21 }
22 }
Компиляция
В том случае, когда при установке пакета в системе были установлены соответствующие пути, для компиляции примера нужно выполнить следующие действия:
UDPClient.java и UDPServer.java ;javac *.java.После компиляции в текущем каталоге должны появиться два
файла: UDPClient.class и UDPServer.class, которые представляют собой наши классы, откомпилированные в байт-код.
Запуск приложения
Первым запускается сервер, для его запуска необходимо, находясь в каталоге, где лежат исполняемые файлы, выполнить команду:
java UDPServer
(рис 3.1) Вывод сервера
(рис 3.2) Вывод клиентаПосле запуска сервер переходит в
Клиент запускается следующим образом:
java UDPClient Hello! 127.0.0.1
Первый параметр определяет строку, которая будет передана серверу, второй - адрес узла, на котором запущен сервер. В данном случае и клиент, и сервер запущены на одной машине, поэтому в качестве адреса используется адрес 127.0.0.1 ( localhost ).
После запуска клиент отправляет строку на
Другим подходом к организации взаимодействия является использование API, основанных на идеологии TCP-Streams. В отличие от UDP, это TCP -сокет,
извлекает из него связанный с ним поток (с сокетом связано два потока - поток ввода и поток вывода) и пишет (читает) данные в поток (из потока). В отличие от подхода, основанного на использовании датаграмм, в этом случае не накладывается никаких ограничений на
Классы:
Ниже приведена простая программа-клиент, иллюстрирующая использование рассмотренных классов.
TCPClient
1 import java.net.*;
2 import java.io.*;
3 public class TCPClient {
4 public static void main (String args[]) {
5 Socket s = null;
6 try {
7 int serverPort = 7896;
8 s = new Socket(args[1], serverPort);
9 DataInputStream in = new DataInputStream( s.getInputStream());
10 DataOutputStream out =new DataOutputStream( s.getOutputStream());
11 out.writeUTF(args[0]); // UTF is a string encoding
12 String data = in.readUTF(); // read a line of data from the stream
13 System.out.println("Received: "+ data) ;
14 } catch (UnknownHostException e) {System.out.println("Socket:" + e.getMessage());
15 } catch (EOFException e) {System.out.println("EOF:" +e.getMessage());
16 } catch (IOException e) {System.out.println("readline:" +e.getMessage());
// error in reading the stream
17 } finally {
18 if(s!=null) try {s.close();} catch (IOException e)
{System.out.println("close:" + e.getMessage());}}
19 }
20 }
В первых строках (строки 1,2) программы (пример 3.3) импортируются пакеты java.net и java.io - соответственно, пакет, содержащий API TCP, и пакет, содержащий классы ввода-вывода. Класс TCPClient имеет единственный метод main,который также является точкой входа в клиентскую программу.
Метод принимает аргументы (try - catch.
Переменная s, объявленная в строке 5, инициализируется в строке 8, где создается соединение с сервером. Для соединения требуются два параметра - имя сервера и порт. Имя сервера передано нам из командной строки, порт нам известен. В строках 9 и 10 создаются потоки ввода и вывода, с помощью которых можно взаимодействовать с сервером - передавать и принимать данные. В данном случае используются не базовые классы InputStream и OutputStream,а их более специализированные потомки DataInputStream и DataOutputStream,
которые содержат готовые методы чтения/записи для всех Java. Затем производится отправка полученной строки на сервер, после чего клиент ожидает получения информации от сервера (строка 12). Метод чтения данных из потока - блокирующий. Прочитав строку, посланную сервером, клиент печатает ее на экране и завершает работу.
Здесь стоит сделать небольшое отступление. Дело в том, что нам очень повезло: в нашем примере и клиент, и сервер реализованы на одной и той же программной платформе - на java. В реальных ситуациях дело может осложняться тем, что различные компоненты распределенного приложения могут быть реализованы на различных не только программных, но и аппаратных платформах. При передаче данных между такими "разными" компонентами возникает проблема их интерпретации - ведь форматы представления могут быть различными для разных платформ.
Представление данных в распределенных системах
Этот небольшой раздел посвящен обсуждению проблемы, возникающей при передаче данных в распределенных системах, компоненты которых функционируют в рамках различных программно-аппаратных платформ.
В предыдущем разделе мы научились передавать пакеты данных между процессами системы. Теперь стоит поподробнее взглянуть на те данные, которые мы передаем. Нужно заметить, что передачу данных между различными компонентами распределенной системы можно представить как "перенос" некой области памяти между процессами (для простоты считаем, что каждый компонент выполнен в виде процесса), которые могут функционировать на различных физических узлах. Передаваемые данные (т.е. переменные, массивы, структуры и т.д.) определяются внутри процесса и
существуют в локальной памяти процесса. Если говорить о данных внутри процесса, то они имеют какую-то int, float, char ) могут иметь различное представление на различных платформах. Таким образом, кроме работы собственно по передаче данных, необходимо еще выполнить работу по их правильной интерпретации после приема.
Для решения этой проблемы обычно применяют один из двух подходов. Первый состоит в том, чтобы передавать данные в представлении посылающей либо принимающей стороны. В этом случае необходимо дополнительное преобразование данных при их передаче либо при их приеме. Второй подход состоит в использовании некоего "внешнего" представления данных, вообще говоря, отличного от представления как отправителя, так и получателя. В этом случае для передачи необходимо выполнить уже два преобразования,
а не одно: сначала на передающей стороне преобразовать данные из локального представления передающей стороны во "внешнее" (транспортное), затем на принимающей - преобразовать "внешнее" представление в
Несмотря на кажущуюся избыточность, второй подход применяется чаще в силу его универсальности. В настоящее время разработано множество таких "внешних" транспортных форматов - SUN Microsystems , , и др. Обычно эти задачи возлагаются на слой , и соответствующие преобразования для программиста прозрачны.
Однако если никакое
TCPServer
Сервер, принимающий данные от клиента и посылающий ему ответ, выполнен следующим образом (пример 3.4).
1 import java.net.*;
2 import java.io.*;
3 public class TCPServer {
4 public static void main (String args[]) {
5 try {
6 int serverPort = 7896; // the server port
7 ServerSocket listenSocket = new ServerSocket (serverPort); // new server port generated
8 while(true) {
9 Socket clientSocket = listenSocket.accept(); // listen for new connection
10 Connection c = new Connection(clientSocket); // launch new thread
11 }
12 } catch(IOException e) { System.out.println("Listen socket:"+e.getMessage());
13 }
14 }
15 }
Как и клиенту, серверу необходима реализация сетевого ввода/вывода, поэтому он импортирует соответствующие пакеты - строки 1,2. Так же как и клиент, сервер состоит из одного метода - main,выполняющего всю работу и одновременно являющегося точкой API позволяет серверу одновременно обслуживать несколько клиентов, поскольку для каждого клиента после его подсоединения создается собственный канал типа "точка-точка".
Класс Connection
1 class Connection extends Thread {
2 DataInputStream in;
3 DataOutputStream out;
4 Socket clientSocket;
5 public Connection (Socket aClientSocket) {
6 try {
7 clientSocket = aClientSocket;
8 in = new DataInputStream( clientSocket.getInputStream());
9 out = new DataOutputStream( clientSocket.getOutputStream());
10 this.start();
11 } catch(IOException e){System.out.println ("Connection:"+e.getMessage());
12 }
13 }
14 public void run() { // an echo server
15 try {
16 String data = in.readUTF(); // read a line of data from the stream
17 out.writeUTF(data); // write a line to the stream
18 clientSocket.close();
19 } catch (EOFException e){System.out.println ("EOF:"+e.getMessage());
20 } catch (IOException e) {System.out.println ("readline:"+e.getMessage());}
21 }
22 }
Класс Connection (пример 3.5) служит для обслуживания отдельного клиента. Мы расположили его в том же исходном файле, что и класс TCPServer,поэтому он не импортирует библиотеки java.net и java.io. Этот класс является потомком класса Thread,т.е. является классом-потоком. В конструктор класса передается сокет, который вернул метод accept серверного сокета, - с его помощью этот класс будет взаимодействовать с клиентом. В конструкторе создаются соответствующие потоки ввода-вывода и запускается поток (поскольку класс сам является потоком, он запускает себя).
Все дальнейшие действия по обслуживанию клиента будут производиться в методе run (строки 14-22). Метод run,как правило, представляет собой вечный цикл, выход из которого осуществляется либо по ошибке сетевого ввода-вывода, либо по специальной команде, передаваемой клиентом и извещающей сервер об окончании работы. В данном случае класс выполняет только два действия - чтение одной строки с клиента и обратную передачу этой же строки. После выполнения этих действий сокет закрывается, поскольку никаких данных передавать по нему более не предполагается и метод run завершается, что означает завершение потока обслуживания клиента.
Компиляция
Для компиляции примера нужно выполнить следующие действия:
После компиляции в текущем каталоге должны появиться файлы с расширением . class, компиляция должна завершиться без ошибок.
Запуск приложения
Первым запускается сервер, для его запуска необходимо выполнить команду:
java TCPServer
После запуска сервер переходит в
(рис 3.3) Вывод сервераКлиент запускается следующим образом:
java TCPClient TCPHello! 127.0.0.1
где первый параметр определяет строку, которая будет передана серверу, второй - адрес узла на котором запущен сервер. В данном случае и клиент, и сервер запущены на одной машине, поэтому в качестве адреса используется адрес 127.0.0.1 ( localhost ).
После запуска клиент отправляет строку на
(рис 3.4) Вывод клиентаТеперь, освоив необходимые технологии, мы можем приступить к первой реализации системы, решающей поставленную нами ранее задачу.
Рабочий каталог расположен в
Итак, мы собираемся написать наше первое распределенное приложение. Способ, с помощью которого мы собираемся это сделать, - самый простой. Раз нам нужно передавать данные между компонентами нашего приложения по сети, следовательно, этому нужно научиться. Первый способ, который мы используем для решения задачи, будет основываться на входящем в состав пакета java.net API. Этот пакет предоставляет возможность для реализации сетевого взаимодействия с применением одного из двух транспортных протоколов: UDP и TCP.
Архитектура протоколов TCP/IP, известная как набор протоколов TCP/IP, возникла в результате исследований в области протоколов и разработок, выполнявшейся в экспериментальной , которая была основана Управлением перспективных исследовательских программ Министерства обороны США ( )[3.26]. Этот набор протоколов состоит из большого собрания протоколов, изданных Координационным советом по сети Internet ( в Internet.
В общем можно сказать, что в обмене информацией всегда принимают участие три агента: приложения, узлы (компьютеры) и сети. К приложениям относятся программы, предназначенные, например, для передачи файлов или электронной почты. Приложения, о которых идет речь, являются распределенными приложениями, включающими в себя обмен данными между двумя и более компьютерными системами. Компьютеры соединены между собой в сети, и предназначенные для обмена данные передаются по сети от одного компьютера к другому. Таким образом, передача данных от одного приложения другому (или от одного компонента другому в рамках одного распределенного приложения) включает в себя, во-первых, получение данных тем узлом, на котором находится приложение-адресат, и, во-вторых, получение данных именно тем выполняющимся на узле-адресате приложением, которому они предназначены (одновременно на узле могут выполняться несколько приложений).
Ввиду этого в задаче обмена информацией выделяют пять основных относительно независимых уровней:
На физическом уровне находится физический интерфейс между устройством передачи данных и передающей средой. На этом уровне задаются характеристики передающей среды, природа сигналов, скорость передачи данных и т.д.
Уровень доступа к сети связан с обменом данными между конечной системой (рабочей станцией, сервером и т.д.) и сетью, к которой подсоединена эта система. Узел-отправитель должен передать в сеть адрес узла-адресата, чтобы сеть могла направить данные по месту требования.
Уровень доступа к сети рассматривается в связи с доступом к сети и маршрутизацией данных между двумя подключенными к сети оконечными системами. В тех случаях, когда устройства подключены к разным сетям, нужны дополнительные процедуры, позволяющие данным переходить из одной сети в другую. Такие функции относятся к межсетевому уровню. На этом уровне функции межсетевой маршрутизации предоставляются с помощью Internet -протокола ( IP ). Internet -протокол может быть реализован не только в конечных системах, но и в маршрутизаторах.
Независимо от природы приложений обмен данными должен быть надежным, т.е. хотелось бы иметь уверенность в том, что все данные попали к приложению-адресату и что эти данные получены в том порядке, в котором они были отправлены. Механизмы обеспечения надежности, по сути, независимы от природы приложений, таким образом, имеет смысл выделить такие механизмы в один общий уровень, совместно используемый всеми приложениями. Этот уровень называется транспортным. Чаще всего для него применяется ).
Наконец, в уровень приложений заложена логика, необходимая для обеспечения работы различных пользовательских приложений. Приложению каждого вида нужен отдельный модуль, специально предназначенный для этого приложения. Именно разработкой уровня приложения мы в дальнейшем и займемся.
Итак, чтобы обмен информацией был возможен, каждый элемент системы должен иметь уникальный адрес. Фактически, нужно указать два уровня адресации. Каждый TCP ) доставить данные нужному процессу. Этот адрес - адрес процесса - в терминологии протоколов семейства TCP/IP называют портом.
Для большинства приложений, выполняющихся в рамках архитектуры протокола TCP/IP, протоколом транспортного уровня выступает TCP. Этот протокол обеспечивает надежное соединение для передачи данных от одного приложения к другому. Кроме него существует еще один широко используемый протокол транспортного уровня, входящий в набор протоколов TCP/IP: пользовательский протокол датаграмм ( User ).
Протокол UDP предоставляет сервис без установления соединения, предназначенный для процедур на уровне приложения; он не гарантирует доставку, сохранение последовательности при приеме переданных пакетов или защиту от их дублирования. Он позволяет процессу отправлять сообщения другим процессам, с помощью минимального протокольного механизма. Протокол UDP выполняет крайне ограниченный набор функций, так как работает без установления соединения. По сути, он добавляет к протоколу IP некоторые возможности адресации портов.
Рассмотрим вкратце возможности, которые предоставляет нам java и ее пакет java.net.
Начнем мы с API, использующего протокол UDP. UDP является протоколом, применяющим для передачи датаграммы. Как уже говорилось, этот протокол не является надежным, поскольку сообщения, передаваемые с его помощью, могут теряться или приходить в другой последовательности, отличной от последовательности их отправки. Таким образом, для обеспечения надежной передачи необходимо организовывать надстройку над этим протоколом, обеспечивающую, например, нумерацию пакетов,
повторную передачу пакетов при истечении времени ожидания и т.д. Длина одного сообщения (одной датаграммы) при использовании этого протокола ограничена 65536 байтами (причем многие реализации вообще ограничивают размер датаграммы 8К), в случае необходимости пересылки порции данных большего размера они должны быть разбиты на куски отправителем и снова собраны получателем. Передача сообщения - не блокирующая, прием - блокирующий с возможностью прерывания по истечении времени ожидания.
Для работы с UDP в пакете java.net определены следующие классы.
IP -адрес узла и порт). Класс предназначен для представления единичной дата-граммы (сообщения). Этот класс используется как для создания сообщения с целью последующей его отправки, так и при приеме сообщения (функция приема возвращает экземпляр этого класса).UDP -дата-грамм. Один из конструкторов принимает в качестве аргумента порт, с которым связывается сокет, другой конструктор, без аргументов, задействует в качестве порта первый попавшийся свободный порт. Класс имеет методы send и receive, для, соответственно, передачи и приема датаграмм. Метод setSoTimeout устанавливает тайм-аут для операций сокета.Ниже приведены две простые программы, использующие рассмотренные механизмы для организации взаимодействия.
UDPClient
Первая программа создает сокет, соединяется с сервером (порт 6789), пересылает ему сообщение и ждет ответа (пример 3.1).
1 import java.net.*;
2 import java.io.*;
3 public class UDPClient{
4 public static void main(String args[]){
5 // аргументы - сообщение и адрес сервера
6 try {
7 DatagramSocket aSocket = new DatagramSocket(); // create socket
8 byte [] message = args[0].getBytes();
9 InetAddress aHost = InetAddress.getByName(args[1]); // DNS lookup
10 int serverPort = 6789;
11 DatagramPacket request =
12 new DatagramPacket(message, args[0].length(), aHost, serverPort);
13 aSocket.send(request);
14 //send message
15 byte[] buffer = new byte[1000];
16 DatagramPacket reply = new DatagramPacket(buffer, buffer.length);
17 aSocket.receive(reply);
18 //wait for reply
19 System.out.println("Reply: " + new String(reply.getData()));
20 aSocket.close();
21 } catch (SocketException e){ System.out.println("Socket: " + e.getMessage());
22 // socket creation failed
23 } catch (IOException e){ System.out.println("IO: " + e.getMessage());
24 // ошибка при приеме
25 }
26 }
27 }
Поскольку это первые наши программы, прокомментируем их более подробно.
В первых строках (строки 1,2) происходит импорт необходимых библиотек (пакетов) классов - в нашем случае это java.net и java.io. Первый пакет содержит необходимые нам классы для работы с UDP, второй определяет необходимые классы ввода/вывода.
Наш класс (строка 3) называется UDPClient,он содержит единственный статический метод main (строка 4), являющийся точкой
UDPServer
Другая программа (сервер) создает сокет и обслуживает запросы клиента (пример 3.2).
1 import java.net.*;
2 import java.io.*;
3 public class UDPServer{
4 public static void main(String args[]) {
5 DatagramSocket aSocket = null;
6 try{
7 aSocket = new DatagramSocket(6789); // create socket at port
8 byte[] buffer = new byte[1000];
9 while(true){
10 DatagramPacket request = new DatagramPacket(buffer, buffer.length);
11 aSocket.receive(request);
12 DatagramPacket reply =
13 new DatagramPacket(request.getData(), request.getLength(),
14 request.getAddress(), request.getPort());
15 aSocket.send(reply);
16 }
17 } catch (SocketException e) {System.out.println("Socket: " + e.getMessage());
18 // socket creation failed
19 } catch (IOException e) {System.out.println("IO: " + e.getMessage());
20 } finally {if(aSocket != null) aSocket.close();}
21 }
22 }
Компиляция
В том случае, когда при установке пакета в системе были установлены соответствующие пути, для компиляции примера нужно выполнить следующие действия:
UDPClient.java и UDPServer.java ;javac *.java.После компиляции в текущем каталоге должны появиться два
файла: UDPClient.class и UDPServer.class, которые представляют собой наши классы, откомпилированные в байт-код.
Запуск приложения
Первым запускается сервер, для его запуска необходимо, находясь в каталоге, где лежат исполняемые файлы, выполнить команду:
java UDPServer
(рис 3.1) Вывод сервера
(рис 3.2) Вывод клиентаПосле запуска сервер переходит в
Клиент запускается следующим образом:
java UDPClient Hello! 127.0.0.1
Первый параметр определяет строку, которая будет передана серверу, второй - адрес узла, на котором запущен сервер. В данном случае и клиент, и сервер запущены на одной машине, поэтому в качестве адреса используется адрес 127.0.0.1 ( localhost ).
После запуска клиент отправляет строку на
Другим подходом к организации взаимодействия является использование API, основанных на идеологии TCP-Streams. В отличие от UDP, это TCP -сокет,
извлекает из него связанный с ним поток (с сокетом связано два потока - поток ввода и поток вывода) и пишет (читает) данные в поток (из потока). В отличие от подхода, основанного на использовании датаграмм, в этом случае не накладывается никаких ограничений на
Классы:
Ниже приведена простая программа-клиент, иллюстрирующая использование рассмотренных классов.
TCPClient
1 import java.net.*;
2 import java.io.*;
3 public class TCPClient {
4 public static void main (String args[]) {
5 Socket s = null;
6 try {
7 int serverPort = 7896;
8 s = new Socket(args[1], serverPort);
9 DataInputStream in = new DataInputStream( s.getInputStream());
10 DataOutputStream out =new DataOutputStream( s.getOutputStream());
11 out.writeUTF(args[0]); // UTF is a string encoding
12 String data = in.readUTF(); // read a line of data from the stream
13 System.out.println("Received: "+ data) ;
14 } catch (UnknownHostException e) {System.out.println("Socket:" + e.getMessage());
15 } catch (EOFException e) {System.out.println("EOF:" +e.getMessage());
16 } catch (IOException e) {System.out.println("readline:" +e.getMessage());
// error in reading the stream
17 } finally {
18 if(s!=null) try {s.close();} catch (IOException e)
{System.out.println("close:" + e.getMessage());}}
19 }
20 }
В первых строках (строки 1,2) программы (пример 3.3) импортируются пакеты java.net и java.io - соответственно, пакет, содержащий API TCP, и пакет, содержащий классы ввода-вывода. Класс TCPClient имеет единственный метод main,который также является точкой входа в клиентскую программу.
Метод принимает аргументы (try - catch.
Переменная s, объявленная в строке 5, инициализируется в строке 8, где создается соединение с сервером. Для соединения требуются два параметра - имя сервера и порт. Имя сервера передано нам из командной строки, порт нам известен. В строках 9 и 10 создаются потоки ввода и вывода, с помощью которых можно взаимодействовать с сервером - передавать и принимать данные. В данном случае используются не базовые классы InputStream и OutputStream,а их более специализированные потомки DataInputStream и DataOutputStream,
которые содержат готовые методы чтения/записи для всех Java. Затем производится отправка полученной строки на сервер, после чего клиент ожидает получения информации от сервера (строка 12). Метод чтения данных из потока - блокирующий. Прочитав строку, посланную сервером, клиент печатает ее на экране и завершает работу.
Здесь стоит сделать небольшое отступление. Дело в том, что нам очень повезло: в нашем примере и клиент, и сервер реализованы на одной и той же программной платформе - на java. В реальных ситуациях дело может осложняться тем, что различные компоненты распределенного приложения могут быть реализованы на различных не только программных, но и аппаратных платформах. При передаче данных между такими "разными" компонентами возникает проблема их интерпретации - ведь форматы представления могут быть различными для разных платформ.
Представление данных в распределенных системах
Этот небольшой раздел посвящен обсуждению проблемы, возникающей при передаче данных в распределенных системах, компоненты которых функционируют в рамках различных программно-аппаратных платформ.
В предыдущем разделе мы научились передавать пакеты данных между процессами системы. Теперь стоит поподробнее взглянуть на те данные, которые мы передаем. Нужно заметить, что передачу данных между различными компонентами распределенной системы можно представить как "перенос" некой области памяти между процессами (для простоты считаем, что каждый компонент выполнен в виде процесса), которые могут функционировать на различных физических узлах. Передаваемые данные (т.е. переменные, массивы, структуры и т.д.) определяются внутри процесса и
существуют в локальной памяти процесса. Если говорить о данных внутри процесса, то они имеют какую-то int, float, char ) могут иметь различное представление на различных платформах. Таким образом, кроме работы собственно по передаче данных, необходимо еще выполнить работу по их правильной интерпретации после приема.
Для решения этой проблемы обычно применяют один из двух подходов. Первый состоит в том, чтобы передавать данные в представлении посылающей либо принимающей стороны. В этом случае необходимо дополнительное преобразование данных при их передаче либо при их приеме. Второй подход состоит в использовании некоего "внешнего" представления данных, вообще говоря, отличного от представления как отправителя, так и получателя. В этом случае для передачи необходимо выполнить уже два преобразования,
а не одно: сначала на передающей стороне преобразовать данные из локального представления передающей стороны во "внешнее" (транспортное), затем на принимающей - преобразовать "внешнее" представление в
Несмотря на кажущуюся избыточность, второй подход применяется чаще в силу его универсальности. В настоящее время разработано множество таких "внешних" транспортных форматов - SUN Microsystems , , и др. Обычно эти задачи возлагаются на слой , и соответствующие преобразования для программиста прозрачны.
Однако если никакое
TCPServer
Сервер, принимающий данные от клиента и посылающий ему ответ, выполнен следующим образом (пример 3.4).
1 import java.net.*;
2 import java.io.*;
3 public class TCPServer {
4 public static void main (String args[]) {
5 try {
6 int serverPort = 7896; // the server port
7 ServerSocket listenSocket = new ServerSocket (serverPort); // new server port generated
8 while(true) {
9 Socket clientSocket = listenSocket.accept(); // listen for new connection
10 Connection c = new Connection(clientSocket); // launch new thread
11 }
12 } catch(IOException e) { System.out.println("Listen socket:"+e.getMessage());
13 }
14 }
15 }
Как и клиенту, серверу необходима реализация сетевого ввода/вывода, поэтому он импортирует соответствующие пакеты - строки 1,2. Так же как и клиент, сервер состоит из одного метода - main,выполняющего всю работу и одновременно являющегося точкой API позволяет серверу одновременно обслуживать несколько клиентов, поскольку для каждого клиента после его подсоединения создается собственный канал типа "точка-точка".
Класс Connection
1 class Connection extends Thread {
2 DataInputStream in;
3 DataOutputStream out;
4 Socket clientSocket;
5 public Connection (Socket aClientSocket) {
6 try {
7 clientSocket = aClientSocket;
8 in = new DataInputStream( clientSocket.getInputStream());
9 out = new DataOutputStream( clientSocket.getOutputStream());
10 this.start();
11 } catch(IOException e){System.out.println ("Connection:"+e.getMessage());
12 }
13 }
14 public void run() { // an echo server
15 try {
16 String data = in.readUTF(); // read a line of data from the stream
17 out.writeUTF(data); // write a line to the stream
18 clientSocket.close();
19 } catch (EOFException e){System.out.println ("EOF:"+e.getMessage());
20 } catch (IOException e) {System.out.println ("readline:"+e.getMessage());}
21 }
22 }
Класс Connection (пример 3.5) служит для обслуживания отдельного клиента. Мы расположили его в том же исходном файле, что и класс TCPServer,поэтому он не импортирует библиотеки java.net и java.io. Этот класс является потомком класса Thread,т.е. является классом-потоком. В конструктор класса передается сокет, который вернул метод accept серверного сокета, - с его помощью этот класс будет взаимодействовать с клиентом. В конструкторе создаются соответствующие потоки ввода-вывода и запускается поток (поскольку класс сам является потоком, он запускает себя).
Все дальнейшие действия по обслуживанию клиента будут производиться в методе run (строки 14-22). Метод run,как правило, представляет собой вечный цикл, выход из которого осуществляется либо по ошибке сетевого ввода-вывода, либо по специальной команде, передаваемой клиентом и извещающей сервер об окончании работы. В данном случае класс выполняет только два действия - чтение одной строки с клиента и обратную передачу этой же строки. После выполнения этих действий сокет закрывается, поскольку никаких данных передавать по нему более не предполагается и метод run завершается, что означает завершение потока обслуживания клиента.
Компиляция
Для компиляции примера нужно выполнить следующие действия:
После компиляции в текущем каталоге должны появиться файлы с расширением . class, компиляция должна завершиться без ошибок.
Запуск приложения
Первым запускается сервер, для его запуска необходимо выполнить команду:
java TCPServer
После запуска сервер переходит в
(рис 3.3) Вывод сервераКлиент запускается следующим образом:
java TCPClient TCPHello! 127.0.0.1
где первый параметр определяет строку, которая будет передана серверу, второй - адрес узла на котором запущен сервер. В данном случае и клиент, и сервер запущены на одной машине, поэтому в качестве адреса используется адрес 127.0.0.1 ( localhost ).
После запуска клиент отправляет строку на
(рис 3.4) Вывод клиентаТеперь, освоив необходимые технологии, мы можем приступить к первой реализации системы, решающей поставленную нами ранее задачу.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.