Протокол UDP
UDP представляет собой альтернативу ТСР, требующую меньших накладных расходов. В отличие от ТСР, UDP имеет следующие характеристики:
- ненадежный сетевой протокол. UDP не имеет ни встроенного механизма обнаружения ошибок, ни средств повторной пересылки поврежденных или потерянных данных;
- без установления логического соединения. Перед пересылкой данных UDP не устанавливает логического соединения. Информация пересылается в предположении, что принимающая сторона ее ожидает;
- основан на сообщениях. Позволяет приложениям пересылать информацию в виде сообщений, передаваемых посредством дейтаграмм (datagram), которые являются единицами передачи данных в UDP.
Как и в ТСР, в UDP применяется схема адресации с использованием портов, позволяющая нескольким приложениям параллельно принимать и посылать данные. В то же время порты UDP отличаются от портов ТСР. Например, одно приложение может отзываться на номер 512 порта UDP, а при этом другой независимый сервис может обрабатывать порт 512, относящийся к ТСР.
Характеристика сокетов UDP
UDP существенно отличается от ТСР. Наиболее подходящая для UDP аналогия — связь посредством почтовых открыток.
В протоколе UDP диалог должен быть разделен на небольшие сообщения, которые умещаются в небольшой пакет определенного размера. Когда посылается сообщение, нельзя быть уверенным, что ответ будет получен: сообщение могло быть потеряно по пути, мог потеряться ответ получателя, получатель также мог игнорировать сообщение.
Почтовые открытки, которыми обмениваются сетевые программы, называются дейтаграммами (datagrams). Дейтаграмма содержит массив байт. Принимающая программа может извлечь этот массив и декодировать информацию, а затем, возможно, послать ответную дейтаграмму.
Как и для протокола ТСР, программирование для UDP будет использовать абстракцию сокета, но сокеты UDP сильно отличаются от сокетов ТСР. Если продолжить почтовую аналогию, то сокет UDP соответствует почтовому ящику.
Почтовый ящик идентифицируется адресом владельца, но нет необходимости заводить новый ящик для каждого, кому нужно посылать сообщения (можно, однако, создать отдельный ящик для газет, чтобы они не попадали в ящик для писем). Для посылки сообщения достаточно написать на открытке адрес, по которому она должна быть доставлена. Затем она помещается в почтовый ящик и (раньше или позже) уходит по назначению.
Можно, в принципе, бесконечно долго ожидать, пока сообщение дойдет до почтового ящика. Когда сообщение получено, его можно прочесть. На открытке содержится также метаинформация, позволяющая по обратному адресу получить сведения об отправителе сообщения.
Итак, программирование с использованием UDP требует решить следующие задачи: создание правильно адресованной дейтаграммы, создание сокета для рассылки и получения дейтаграмм данным приложением, помещение дейтаграмм в сокет для передачи по назначению, ожидание получения дейтаграмм из сокета, декодирование дейтаграмм для выделения самого сообщения, адреса отправителя и другой метаинформации.
Классы UDP
Необходимые средства поддержки протокола UDP находятся в пакете java.net. Для создания дейтаграмм в Java существует класс DatagramPacket. При получении дейтаграммы по протоколу UDP класс DatagramPacket используется также для чтения данных, адреса отправителя и метаинформации.
Чтобы создать дейтаграмму для отправки на удаленную машину, используется следующий конструктор:
public DatagramPacket(byte[] ibuf, int length, InetAddress iaddr, int iport);
Чтобы получить дейтаграмму, необходимо использовать другой конструктор для объекта DatagramPacket, в котором будут находиться принятые данные. Прототип конструктора имеет вид.
public DatagramPacket(byte[] ibuf, int length);
Дейтаграммы не ограничены определенной длиной; можно создавать как очень длинные, так и очень короткие дейтаграммы. Заметим, однако, что между клиентом и сервером должно существовать соглашение о длине дейтаграмм, поскольку они оба должны создать массив байт нужного размера перед созданием объекта DatagramPacket для посылки или получения дейтаграммы.
Когда дейтаграмма получена, как будет продемонстрировано ниже, можно прочитать ее данные. Другие методы позволяют получить метаинформацию, относящуюся к сообщению:
- public int getLength(); – возвращает количество байт, из которых состоят данные дейтаграммы;
- public byte[] getData(); – позволяет получить массив, содержащий эти данные;
- public InetAddress getAddress(); – возвращает адрес отправителя;
- public int getPort(); – возвращает номер порта UDP, используемый отправителем.
public DatagramSocket() throws SocketException;
public DatagramSocket(int port) throws SocketException;
Сокет, созданный первым конструктором, можно использовать для отправки правильно адресованных дейтаграмм при помощи следующего метода класса DatagramSocket:
public void send(DatagramPacket p) throws IOException;
Если DatagramSocket создан вторым конструктором, можно получить дейтаграмму:
public syncronized void receive(DatagramPacket p) throws IOException;
Когда закончен обмен через сокет UDP, его следует закрыть методом:
public syncronized void close();
Листинг приложения сервера:
import java.net.*;
import java.io.*;
public class UDPServer {
public final static int DEFAULT_PORT = 8001;//определение порта
//сервера
public final String VERSION_CMD = "VERS";//определение версии
//команды
public final String QUIT_CMD = "QUIT";//определение
//команды «выход»
public final byte[] VERSION = { 'V', '2', '.', '0' };//создание массива
//для определения версии сервера
public final byte[] UNKNOWN_CMD = { 'U', 'n', 'k', 'n', 'o', 'w', 'n', ' ',
'c', 'o', 'm', 'm', 'a', 'n', 'd' };//неизвестная команда
public void runServer() throws IOException {//метод сервера runServer
DatagramSocket s = null;//создание объекта DatagramSocket
try {
boolean stopFlag = false;//создание флага stopFlag и его инициализация
//значением false
byte[] buf = new byte[512];//буфер для приема/передачи дейтаграммы
s = new DatagramSocket(DEFAULT_PORT);//привязка сокета к
//реальному объекту с портом DEFAULT_PORT
System.out.println("UDPServer: Started on " + s.getLocalAddress() + ":"
+ s.getLocalPort());//вывод к консоль сообщения
while(!stopFlag) {//цикл до тех пор, пока флаг не примет значение true
DatagramPacket recvPacket = new DatagramPacket(buf, buf.length);
//создание объекта дейтаграммы для получения данных
s.receive(recvPacket);//помещение полученного содержимого в
//объект дейтаграммы
String cmd = new String(recvPacket.getData()).trim();//извлечение
//команды из пакета
System.out.println("UDPServer: Command: " + cmd);
DatagramPacket sendPacket = new DatagramPacket(buf, 0, recvPacket.getAddress(), recvPacket.getPort());
//формирование объекта
// дейтаграммы для отсылки данных
int n = 0;//количество байт в ответе
if (cmd.equals(VERSION_CMD)) {//проверка версии команды
n = VERSION.length;
System.arraycopy(VERSION, 0, buf, 0, n);
}
else if (cmd.equals(QUIT_CMD)) {
stopFlag = true;//остановка сервера
continue;
}
else {
n = UNKNOWN_CMD.length;
System.arraycopy(UNKNOWN_CMD, 0, buf, 0, n);
}
sendPacket.setData(buf);//установить массив посылаемых данных
sendPacket.setLength(n);//установить длину посылаемых данных
s.send(sendPacket);//послать сами данные
} // while(server is not stopped)
System.out.println("UDPServer: Stopped");
}
finally {
if (s != null) {
s.close();//закрытие сокета сервера
}
}
}
public static void main(String[] args) {//метод main
try {
UDPServer udpSvr = new UDPServer();//создание объекта udpSvr
udpSvr.runServer();//вызов метода объекта runServer
}
catch(IOException ex) {
ex.printStackTrace();
}
}
}
Листинг приложения клиента:
import java.net.*;
import java.io.*;
public class UDPClient {//описание класса клиента
public void runClient() throws IOException {//метод клиента runClient
DatagramSocket s = null;//создание дейтаграммы
try {
byte[] buf = new byte[512]; //буфер для приема/передачи дейтаграммы
s = new DatagramSocket();//привязка сокета к реальному объету
System.out.println("UDPClient: Started");
byte[] verCmd = { 'V', 'E', 'R', 'S' };
DatagramPacket sendPacket = new DatagramPacket(verCmd, verCmd.length, InetAddress.getByName("127.0.0.1"), 8001);
//создание
//дейтаграммы для отсылки данных
s.send(sendPacket);//посылка дейтаграммы
DatagramPacket recvPacket = new DatagramPacket(buf, buf.length);//создание дейтаграммы для получения данных
s.receive(recvPacket);//получение дейтаграммы
String version = new String(recvPacket.getData()).trim();//извлечение
//данных (версии сервера)
System.out.println("UDPClient: Server Version: " + version);
byte[] quitCmd = { 'Q', 'U', 'I', 'T' };
sendPacket.setData(quitCmd);//установить массив посылаемых данных
sendPacket.setLength(quitCmd.length);//установить длину посылаемых
// данных
s.send(sendPacket); //послать данные серверу
System.out.println("UDPClient: Ended");
}
finally {
if (s != null) {
s.close();//закрытие сокета клиента
} } }
public static void main(String[] args) {//метод main
try {
UDPClient client = new UDPClient();//создание объекта client
client.runClient();//вызов метода объекта client
}
catch(IOException ex) {
ex.printStackTrace();
}
}
}
Вначале запустите сервер, затем клиент. В результате на сервере появятся строки:
UDPServer: Started on 0.0.0.0/0.0.0.0:8001
UDPServer: Command: VERS
UDPServer: Command: QUIT
UDPServer: Stopped
На клиенте появятся строки:
UDPClient: Started
UDPClient: Server Version: V2.0
UDPClient: Ended





