Назад
Вперед
8.9. Посылка широковещательных сообщений
Приложение обеспечивает возможность посылки
широковещательных (broadcast) сообщений по сети TCP/IP.
Выполняет одновременно роль клиента и сервера.
Пример демонстрирует использование классов
DatagramSocket и DatagramPacket.
Исходный текст примера
Архив проекта для Java WorkShop 2.0
Немного теории
Датаграммные сокеты не гарантируют доставку
пакетов данных, но работают быстрее потоковых и
обеспечивают возможность широковещательной
рассылки пакетов данных одновременно всем узлам
сети. Для работы с такими сокетами приложение
должно создать объект класса DatagramSocket, а также
подготовить объект класса DatagramPacket. В него будет
записан блок данных, принятый от партнера по
сети.
В классе DatagramSocket имеются два конструктора,
прототипы которых представлены ниже:
public DatagramSocket(int port);
public DatagramSocket();
Первый из этих конструкторов позволяет
определить порт для сокета, второй предполагает
использование любого свободного порта.
Канал, а также входные и выходные потоки
создавать не нужно. Данные передаются и
принимаются методами send и receive, определенными в
классе DatagramSocket:
public void send(DatagramPacket p);
public void receive(DatagramPacket p);
В качестве параметра этим методам передается
ссылка на пакет данных (соответственно,
передаваемый и принимаемый), определенный как
объект класса DatagramPacket.
После использования сокет нужно закрыть
методом close:
public void close();
Перед тем как принимать или передавать данные с
использованием методов receive и send вы должны
подготовить объекты класса DatagramPacket. Метод receive
запишет в такой объект принятые данные, а метод
send - перешлет данные из объекта класса DatagramPacket
узлу, адрес которого указан в пакете.
Подготовка объекта класса DatagramPacket для приема
пакетов выполняется с помощью следующего
конструктора:
public DatagramPacket(byte buf[],
int length);
Этому конструктору передается ссылка на массив
buf, в который нужно будет записать данные, и
размер массива length.
Если вам нужно подготовить пакет для передачи,
воспользуйтесь конструктором, который
дополнительно позволяет задать адрес IP iaddr и
номер порта port узла назначения:
public DatagramPacket(byte buf[],
int length,
InetAddress iaddr, int port);
Информация о том, в какой узел и на какой порт
необходимо доставить пакет данных, хранится не в
сокете, а в пакете, то есть в объекте класса
DatagramPacket.
Описание примера
Приложение DatagramChat, исходный текст которого мы
рассмотрим в этом разделе, предназначено для
широковещательной рассылки сообщений в
локальной сети TCP/IP. Оно одновременно выполняет
роль клиента и сервера (рис. 1).
Рис. 1. Главное окно приложения DatagramChat
Сразу после запуска в окне многострочного
текстового редактора, расположенном в левом
верхнем углу главного окна программы, появляется
сообщение об успешном или неуспешном открытии
датаграммного сокета с номером 9997.
Неудача при открытии сокета может произойти в
том случае, если пользователь предпринял попытку
запуска второй копии приложения на одной и той же
рабочей станции, где сокет 9997 уже занят. В таком
случае программа DatagramChat сможет выполнять роль
клиента, передающего широковещательные
сообщения, но сообщения от других рабочих
станций приниматься не будут.
Чтобы послать сообщение на все рабочие станции
сети, его нужно набрать в однострочном редакторе
и затем нажать кнопку Send.
Сообщение будет продублировано в окне
программы с префиксом ":". Его примут все
рабочие станции, в том числе и та, откуда это
сообщение было отправлено. Принимаемые
сообщения отмечаются стрелочкой.
Перейдем к описанию исходного текста.
Главный класс приложения DatagramChat
Метод main, определенный в главном классе
приложения DatagramChat, создает окно приложения как
объект класса FrameWindow:
import java.awt.*;
import java.awt.event.*;
import java.net.*;
import java.io.*;
public class DatagramChat
{
public static void main(String args[])
{
FrameWindow frame;
frame = new FrameWindow(
"Datagram chat");
frame.setVisible(true);
}
}
Это окно затем отображается методом setVisible.
Класс FrameWindow
Данный класс создан на базе класса Frame:
class FrameWindow extends Frame
implements ActionListener, WindowListener
{
. . .
}
В нем мы определили поля, конструктор и
несколько методов.
В полях taConsole и tfCommand хранятся ссылки,
соответственно, на многострочный и однострочный
редакторы текста:
TextArea taConsole;
TextField tfCommand;
Следующие два поля предназначены для хранения
ссылок на кнопки Exit и Save:
Button btExit;
Button btSend;
Для исключения блокирования приложения прием
широковещательных сообщений выполняется в
отдельном потоке. Поле sThread хранит ссылку на
соответствующий класс:
ServerThread sThread = null;
В полях s и dp мы храним ссылки на объекты,
применяемые для отправления широковещательных
сообщений:
static DatagramSocket s;
static DatagramPacket dp;
Конструктор класса FrameWindow
В задачу конструктора входит создание всех
необходимых компонент (редакторов текста и
кнопок), а также добавление этих компонент в
главное окно приложения. Компоненты добавляются
в режиме размещения GridBagLayout.
Заметим, что сразу после инициализации кнопка
Send блокируется конструктором класса FrameWindow:
btSend.setEnabled(false);
После этого конструктор запускает поток приема
сообщений:
sThread = new ServerThread(this);
sThread.start();
И, наконец, последнее действие - получение
датаграммного сокета для приема сообщений:
try
{
s = new DatagramSocket();
}
catch(Exception ex)
{
System.out.println(ex.toString());
}
Метод actionPerformed класса FrameWindow
Когда пользователь нажимает кнопку Send, метод
actionPerformed извлекает из однострочного редактора
текст передаваемого сообщения, выводит его в
окно программы методом consolePrint, а затем передает
методу sendString:
if(e.getSource().equals(btSend))
{
String szSended = tfCommand.getText();
consolePrint("\n:" + szSended);
try
{
sendString(szSended);
}
catch(Exception ex)
{
System.out.println(ex.toString());
}
}
Метод sendString посылает пакет на все рабочие
станции сети с применением датаграммных сокетов.
Метод sendString класса FrameWindow
Исходный текст метода sendString достаточно прост:
static void sendString(String str)
throws IOException
{
byte[] buf = str.getBytes();
dp = new DatagramPacket(buf, buf.length,
InetAddress.getByName("255.255.255.255"),
9997);
s.send(dp);
}
Передаваемая строка сначала преобразуется в
массив байт buf. Заметим, что в массив записываются
символы UNICODE.
Далее метод sendString создает датаграммный пакет
класса DatagramPacket, передавая соответствующему
конструктору ссылку на буфер с данными, размер
этого буфера, а также адрес IP специального вида
для передачи широковещательных пакетов -
255.255.255.255.
Подготовленный пакет передается на сокете s
методом send. Напомним, что сокет s предназначен для
передачи сообщений и создается конструктором
класса FrameWindow.
Класс ServerThread
Класс ServerThread создан на базе класса Thread:
class ServerThread extends Thread
{
. . .
}
В поле frame, определенном в этом классе, мы храним
ссылку на родительское окно:
FrameWindow frame;
Поле s используется для хранения сокета, на
котором выполняется прием широковещательных
сообщений:
DatagramSocket s;
Конструктор класса ServerThread
Конструктор просто сохраняет ссылку на
родительское окно в соответствующем поле:
public ServerThread(FrameWindow fr)
{
frame = fr;
}
Метод run класса ServerThread
Этот метод работает в отдельном потоке и
занимается приемом широковещательных сообщений.
Сразу после инициализации он разблокирует
кнопку Send и затем пытается создать серверный
сокет:
try
{
frame.btSend.setEnabled(true);
s = new DatagramSocket(9997);
. . .
}
catch(SocketException se)
{
frame.consolePrint(
"!Server socket could not be opened\n");
stop();
s.close();
}
catch(Exception ex)
{
stop();
s.close();
}
В процессе создания серверного сокета возможно
возникновение исключения SocketException (например,
если сокет с указанным номером уже занят другой
копией нашего приложения). Соответствующий
обработчик выводит сообщение об ошибке в окно
программы и останавливает поток приема
сообщений.
При удачном получении сокета метод run запускает
цикл чтения поступающих широковещательных
сообщений:
frame.consolePrint(
"!Server socket opend\n");
while(true)
{
String szStr = recvString(s);
frame.consolePrint("\n--->" + szStr);
}
Принятые сообщения отображаются в окне
программы методом consolePrint.
Метод recvString класса ServerThread
Прием широковещательных сообщений выполняется
методом recvString:
static String recvString(DatagramSocket s)
throws IOException
{
DatagramPacket dp;
byte buf[] = new byte[512];
dp = new DatagramPacket(buf, 512);
s.receive(dp);
String szBuf = new String(buf);
return szBuf;
}
Вначале этот метод готовит пакет класса
DatagramPacket, передавая соответствующему
конструктору адрес и размер буфера,
предназначенного для приема данных.
Затем вызывается метод receive, который и
выполняет прием данных.
Принятые данные преобразуются в строку UNICODE и
возвращаются вызывающему методу.
Назад Вперед |