Microsoft Visual J++. Создание приложений и аплетов на языке Java. Часть 2© Александр Фролов, Григорий ФроловТом 32, М.: Диалог-МИФИ, 1997, 288 стр. Приложения DatagramServer и DatagramClientПриложения DatagramServer и DatagramClient иллюстрируют применение датаграммных сокетов для передачи данных от нескольких копий одного и того же клиента одному серверу с известным адресом и номером порта. Клиентские приложения посылают серверу строки, которые пользователь вводит с клавиатуры. Сервер принимает эти строки, отображая их в своем консольном окне вместе с номером порта клиента (рис. 3.4). Рис. 3.4. Передача данных между приложениями DatagramClient и DatagramServer через датаграммный сокет Когда с консоли клиента будет введена строка “quit”, этот клиент и сервер завершает свою работу. Работа остальных клиентов также может быть завершена подобным образом, причем независимо от того, работает сервер, или нет. Наши клиенты не получают от сервера никакого подтверждения в ответ на переданные ему пакеты. Вы можете изменить программу клиента, добавив такую возможность. Однако учтите, что так как датаграммные сокеты не гарантируют доставки пакетов, ожидание ответа может продлиться бесконечно долго. Чтобы избежать этого, ваше приложение должно выполнять работу с сервером в отдельной задаче, причем главная задача должна ждать ответ ограниченное время. Все что вам нужно, чтобы реализовать работу подобным образом, вы найдете в первой главе нашей книги, посвященной мультизадачности в приложениях Java. Исходный текст приложения DatagramServerИсходный текст приложения DatagramServer вы найдете в листинге 3.7. Листинг 3.7. Файл DatagramServer\DatagramServer.java // ========================================================= // Использование датаграммных сокетов // Приложение сервера // // (C) Фролов А.В, 1997 // // E-mail: frolov@glas.apc.org // WWW: http://www.glasnet.ru/~frolov // или // http://www.dials.ccas.ru/frolov // ========================================================= import java.io.*; import java.net.*; import java.util.*; public class DatagramServer { // ------------------------------------------------------- // main // Метод, получающий управление при запуске приложения // ------------------------------------------------------- public static void main(String args[]) { // Массив для ввода строки с клавиатуры byte bKbdInput[] = new byte[256]; // Буфер для чтения команд byte buf[] = new byte[512]; // Сокет сервера DatagramSocket s; // Принимаемый пакет DatagramPacket pinp; // Адрес узла, откуда пришел принятый пакет InetAddress SrcAddress; // Порт, откуда пришел принятый пакет int SrcPort; try { // Выводим строку приглашения System.out.println( "Datagramm Socket Server Application"); } catch(Exception ioe) { // При возникновении исключения выводим его описание // на консоль System.out.println(ioe.toString()); } try { // Создаем сокет сервера s = new DatagramSocket(9998); // Создаем пакет для приема команд pinp = new DatagramPacket(buf, 512); // Цикл обработки команд, полученных от клиента while(true) { // Принимаем пакет от клиента s.receive(pinp); // Получаем адрес узла, приславшего пакет SrcAddress = pinp.getAddress(); // Получаем порт, на котором был передан пакет SrcPort = pinp.getPort(); // Отображаем принятую команду на консоли сервера // Формируем строку из принятого блока String str = new String(buf, 0); // Обрезаем строку, удаляя символ конца строки StringTokenizer st; st = new StringTokenizer(str, "\r\n"); str = new String((String)st.nextElement()); // Выводим строку команды на консоль System.out.println("> " + str + " < " + "port: " + SrcPort); // Если пришла команда 'quit', прерываем цикл if(str.equals("quit")) break; } // Закрываем сокет сервера s.close(); } catch(Exception ioe) { System.out.println(ioe.toString()); } try { System.out.println( "Press <Enter> to terminate application..."); System.in.read(bKbdInput); } catch(Exception ioe) { System.out.println(ioe.toString()); } } } Описание исходного текста приложения DatagramServerВнутри функции main мы определили несколько переменных. Массив bKbdInput хранит строку, введенную с клавиатуры. Массив buf используется для хранения строк (команд), которые сервер получает от клиентских приложений. В переменной s класса DatagramSocket хранится ссылка на датаграмный сокет, который будет использован для приема команд от клиентских приложений. Переменная pinp класса DatagramPacket хранит пакеты, полученные сервером из сети. Переменные SrcAddress (класса InetAddress) и SrcPort типа int хранят, соответственно, адрес и порт узла, отправившего пакет. Первое, что делает сервер - это создание датаграммного сокета: s = new DatagramSocket(9998); Конструктору передается номер порта 9998, на котором сервер будет принимать пакеты данных от клиентских приложений. После создания сокета сервер создает объекта класса DatagramPacket, в который будут записываться приходящие от клиентов пакеты: pinp = new DatagramPacket(buf, 512); Конструктор пакета получает ссылку на массив buf, в который нужно будет записывать приходящие по сети данные, и размер этого массива, равный 512 байт. Далее сервер запускает цикл приема пакетов и отображения их содержимого. Пакет принимается простым вызовом метода receive из класса DatagramSocket: s.receive(pinp); Этот метод блокирует работу вызвавшей его задачи до тех пор, пока по данному сокету не будет получен пакет данных. Когда пакет будет получен, наше приложение определяет адрес и порт отправителя: SrcAddress = pinp.getAddress(); SrcPort = pinp.getPort(); Эта информация может пригодиться, если вы будете посылать отправителю ответный пакет. Наше приложение ответные пакеты не посылает. Оно преобразует принятые данные в текстовую староку класса String, добавляет к этой строке номер порта отправителя и отображает эту информацию на консоли: System.out.println("> " + str + " < " + "port: " + SrcPort); Цикл приема команд завершается, если от клиента пришла строка “quit”: if(str.equals("quit")) break; Перед тем как завершить свою работу, наше приложение закрывает датаграммный сокет, вызывая для этого метод close: s.close(); Исходный текст приложения DatagramClientВ листинге 3.8 приведен исходный текст приложения DatagramClient. Листинг 3.8. Файл DatagramClient\DatagramClient.java // ========================================================= // Использование датаграммных сокетов // Приложение клиента // // (C) Фролов А.В, 1997 // // E-mail: frolov@glas.apc.org // WWW: http://www.glasnet.ru/~frolov // или // http://www.dials.ccas.ru/frolov // ========================================================= import java.io.*; import java.net.*; import java.util.*; public class DatagramClient { // ------------------------------------------------------- // main // Метод, получающий управление при запуске приложения // ------------------------------------------------------- public static void main(String args[]) { // Массив для ввода строки с клавиатуры byte bKbdInput[] = new byte[256]; // Размер введенной строки int length; // Рабочая строка String str; // Сокет клиента DatagramSocket s; // Передаваемый пакет DatagramPacket pout; try { // Выводим строку приглашения System.out.println( "Datagram Socket Client Application" + "\nEnter any string or 'quit' to exit..."); } catch(Exception ioe) { // При возникновении исключения выводим его описание // на консоль System.out.println(ioe.toString()); } try { // Получаем адрес локального узла InetAddress OutAddress = InetAddress.getLocalHost(); // Создаем сокет с использованием любого // свободного порта s = new DatagramSocket(); // Создаем передаваемый пакет pout = new DatagramPacket(bKbdInput, bKbdInput.length, OutAddress, 9998); // Цикл передачи команд серверу while(true) { // Читаем строку команды с клавиатуры length = System.in.read(bKbdInput); // Если строка не пустая, обрабатываем ее if(length != 1) { // Преобразуем строку в формат String str = new String(bKbdInput, 0); // Обрезаем строку, удаляя символ конца строки StringTokenizer st; st = new StringTokenizer(str, "\n"); str = new String((String)st.nextElement()); // Выводим передаваемую строку команды // на консоль для контроля System.out.println("> " + str); // Посылаем пакет серверу s.send(pout); // Если введена команда 'quit', прерываем цикл if(str.equals("quit")) break; } } // Закрываем сокет s.close(); } catch(Exception ioe) { System.out.println(ioe.toString()); } try { System.out.println( "Press <Enter> to terminate application..."); System.in.read(bKbdInput); } catch(Exception ioe) { System.out.println(ioe.toString()); } } } Описание исходного текста приложения DatagramClientВнутри метода main определен массив bKbdInput, предназначенный для хранения данных, введенных с клавиатуры, переменная length, в которой хранится размер этих данных, рабочая строка str класса String, датаграммный сокет s и пакет pout класса DatagramPacket. Прежде всего приложение определяет адрес узла, на котором оно выполняется, вызывая метод getLocalHost: InetAddress OutAddress = InetAddress.getLocalHost(); Этот адрес будет использован для формирования передаваемого пакета данных. Затем клиент создает датаграммный сокет, применяя для этого конструктор без параметров: s = new DatagramSocket(); Напомним, что в этом случае для сокета выделяется любой свободный порт. На следующем шаге приложение формирует передаваемый пакет, вызывая конструктор класса DatagramPacket: pout = new DatagramPacket(bKbdInput, bKbdInput.length, OutAddress, 9998); Этому конструктору указывается адрес массива, содержащего введенные с клавиатуры данные, размер этого массива, адрес локального узла, на который нужно передать пакет, и номер порта серверного приложения. Теперь все готово для запуска цикла передачи команд от клиента к серверу. В этом цикле выполняется чтение строки с клавиатуры, причем размер прочитанной строки сохраняется в переменной length: length = System.in.read(bKbdInput); Далее, если строка не состоит из одного лишь символа перехода на новую строку, она отображается на косоли и посылается серверу методом send: s.send(pout); После того как пользователь введет строку “quit”, цикл завершается. Вслед за этим приложение закрывает датаграммный сокет: s.close(); |