В предходната тема изяснихме как се разработват Java приложения, които си комуникират чрез TCP сокети. В тази тема ще се занимаем със средствата, които платформата Java ни дава за комуникация чрез единични UDP пакети.
Предимствата на протокола UDP
Както знаем, протоколът UDP осигурява изпращане и получаване на единични пакети с данни, пристигането на които не е гарантирано. Поради факта, че не установява надеждна връзка между двете приложения, които комуникират по между си, UDP генерира много по-малък мрежов трафик отколкото TCP и затова по принцип осигурява по-голяма бързина при обмяна на единични съобщения.
Кога да използваме UDP
UDP може да се използва, когато трябва да се изпращат малки по размер и независими едно от друго съобщения. Когато се изпращат обемисти съобщения или ако редът на доставянето на съобщенията е важен, UDP не е подходящ избор.
Протоколът UDP има и още една характерна особеност – той е ненадежден. Успешното изпращане на един UDP пакет не гарантира че той ще пристигне или пък че ако изпратим два UDP пакета един след друг, те ще пристигнат в същия ред, в който са изпратени. Ето защо преди да се вземе решение дали да се използва комуникация по UDP, трябва внимателно да се прецени дали този протокол е подходящ.
Къде се използва UDP в практиката
Типичен пример за използване на UDP протокола е при Интернет услугата DNS. При нея клиентът праща кратка заявка, в която описва за кое име на машина или за кой IP адрес иска информация и каква точно информация (такава заявка се нарича DNS query), а DNS сървърът връща кратък отговор (DNS response) с поисканата информация във вид на единичен UDP пакет.
Забележка: Услугата DNS може да работи и по протокол TCP. Вариантът по TCP е по удобен, ако се прави серия от заявки и се търси гарантираност на отговора, докато за единични заявки е по-удобно и по-бързо да се ползва UDP.
Използване на UDP сокети в Java
В Java за поддръжката на UDP сокети разполагаме с класовете java.net.DatagramSocket и java.net.DatagramPacket. Класът DatagramSocket ни дава възможност да се свързваме (да се bind-ваме) към определен мрежов интерфейс и порт и да изпращаме и получаваме пакети. Класът DatagramPacket реално представлява структура от данни, която описва един UDP пакет.
Да илюстрираме използването на посочените класове чрез един пример. Да си поставим за задача изготвянето на приложение, което при поискване връща на потребителя текущата дата. За получаването на клиентски заявки и изпращането на отговор ще използваме единични UDP пакети. Ето едно възможно решение на поставената задача:
UDPDateServer.java
|
import java.net.*;
import java.util.Date;
public class UDPDateServer {
public static final int LISTENING_UDP_PORT = 12345;
public static final String DATE_REQUEST = "GET DATE";
public static final int RECEIVE_BUFFER_SIZE = 256;
public static void main(String[] args) throws Exception {
// Create UDP socket
DatagramSocket datagramSocket =
new DatagramSocket(LISTENING_UDP_PORT);
System.out.println("UDP Date Server is listening " +
"on port " + LISTENING_UDP_PORT);
while (true) {
// Receive UDP client request
byte[] receiveBuf = new byte[RECEIVE_BUFFER_SIZE];
DatagramPacket packetIn = new
DatagramPacket(receiveBuf, receiveBuf.length);
datagramSocket.receive(packetIn);
String request =
new String(receiveBuf, 0, packetIn.getLength());
// Send response to the client
if (request.equalsIgnoreCase(DATE_REQUEST)) {
String response = new Date().toString();
byte[] responseBuf = response.getBytes();
InetAddress senderIP = packetIn.getAddress();
int senderPort = packetIn.getPort();
DatagramPacket packetOut = new DatagramPacket(
responseBuf, responseBuf.length,
senderIP, senderPort);
datagramSocket.send(packetOut);
}
}
}
}
|
Както се вижда от кода, сървърът отваря един UDP сокет на порт 12345, който се използва както за получаване, така и за изпращане на UDP пакети. След това в безкраен цикъл получава UDP пакет с клиентска заявка (като счита че тя не надвишава 256 байта), извлича от получения пакет IP адреса и порта на изпращача, проверява дали заявката е за извличане на текущата дата, след което създава пакет с отговор (символен низ, съдържащ текущата дата и час) и го изпраща на клиента. Сървърът приема, че клиентът очаква отговора на същия UDP порт, от който е изпратил заявката си. Единствената валидна заявка, на която сървърът отговаря, е за извличане на текущата дата и час. Тази заявка се разпознава по съдържанието на получения UDP пакет, което трябва да е текста „GET DATE”.
Пример за UDP клиент
Нека сега се опитаме да напишем клиент за нашия сървър. Той трябва да изпрати на сървъра UDP пакет, съдържащ заявка за връщане на текущата дата и да слуша известно време за UDP пакет-отговор от сървъра на същия порт, от който е изпратена заявката. Получаването на отговор от сървъра, разбира се, не е гарантирано и затова чакането му трябва да продължи не повече от някакъв предварително зададен времеви лимит (timeout). Ето и примерна реализация:
UDPDateClient.java
|
import java.net.*;
public class UDPDateClient {
public static final String DATE_SERVER = "localhost";
public static final int DATE_PORT = 12345;
public static final String DATE_REQUEST = "GET DATE";
public static final int TIMEOUT_IN_SECONDS = 5;
public static void main(String[] args) throws Exception {
// Send request to the UDP Date Server
DatagramSocket datagramSocket = new DatagramSocket();
String request = DATE_REQUEST;
byte[] requestBuf = request.getBytes();
DatagramPacket packetOut = new DatagramPacket(
requestBuf, requestBuf.length,
InetAddress.getByName(DATE_SERVER), DATE_PORT);
datagramSocket.send(packetOut);
System.out.println("Sent date request to the server.");
// Receive the server response
byte[] responseBuf = new byte[256];
DatagramPacket packetIn =
new DatagramPacket(responseBuf, responseBuf.length);
datagramSocket.setSoTimeout(TIMEOUT_IN_SECONDS * 1000);
try {
datagramSocket.receive(packetIn);
String response = new String(
responseBuf, 0, packetIn.getLength());
System.out.println("Server response: " + response);
} catch (SocketTimeoutException ste) {
System.err.println("Timeout! No response received" +
" in " + TIMEOUT_IN_SECONDS + " seconds.");
}
datagramSocket.close();
}
}
|
Както се вижда от кода, няма нищо сложно. Първо се създава UDP сокет, след това се изпраща UDP пакет със заявка към сървъра и след това се прави опит в рамките на 5 секунди да се получи отговор. Ако се поучи отговор, той се отпечатва, а иначе се отпечатва съобщение за грешка. Грешката означава, че или сървърът не е получил пакета, или го е получил и е пратил отговор, но отговорът се е изгубил. При протокола UDP изгубването на пакет съвсем не е изключено.
Сподели с приятели: |