Руководство по UDP в Java

Руководство по UDP в Java

1. обзор

В этой статье мы рассмотрим сетевое взаимодействие с Java по протоколу пользовательских дейтаграмм (UDP).

UDP - это протокол связи, которыйtransmits independent packets over the network with no guarantee of arrival and no guarantee of the order of delivery.

Большая часть связи через Интернет происходит по протоколу управления передачей (TCP), однако, UDP имеет свое место, которое мы рассмотрим в следующем разделе.

2. Зачем использовать UDP?

UDP сильно отличается от более распространенного протокола TCP. Но прежде чем рассматривать недостатки UDP на поверхностном уровне, важно понять, что отсутствие накладных расходов может сделать его значительно быстрее, чем TCP.

Помимо скорости, мы также должны помнить, что некоторые виды связи не требуют надежности TCP, но вместо этого ценят низкую задержку. Видео является хорошим примером приложения, которое может выиграть от работы по протоколу UDP вместо TCP.

3. Создание приложений UDP

Создание приложений UDP очень похоже на построение системы TCP; Единственное отличие состоит в том, что мы не устанавливаем соединение точка-точка между клиентом и сервером.

Настройка очень проста тоже. Java поставляется со встроенной сетевой поддержкой UDP, которая является частью пакетаjava.net. Поэтому для выполнения сетевых операций через UDP нам нужно только импортировать классы из пакетаjava.net:java.net.DatagramSocket иjava.net.DatagramPacket.

В следующих разделах мы узнаем, как разрабатывать приложения, которые обмениваются данными через UDP; мы будем использовать популярный протокол эха для этого приложения.

Сначала мы создадим эхо-сервер, который отправит обратно любое отправленное ему сообщение, затем эхо-клиент, который просто отправит любое произвольное сообщение на сервер, и, наконец, мы протестируем приложение, чтобы убедиться, что все работает нормально.

4. Сервер

При обмене данными UDP одно сообщение инкапсулируется вDatagramPacket, которое отправляется черезDatagramSocket.

Начнем с настройки простого сервера:

public class EchoServer extends Thread {

    private DatagramSocket socket;
    private boolean running;
    private byte[] buf = new byte[256];

    public EchoServer() {
        socket = new DatagramSocket(4445);
    }

    public void run() {
        running = true;

        while (running) {
            DatagramPacket packet
              = new DatagramPacket(buf, buf.length);
            socket.receive(packet);

            InetAddress address = packet.getAddress();
            int port = packet.getPort();
            packet = new DatagramPacket(buf, buf.length, address, port);
            String received
              = new String(packet.getData(), 0, packet.getLength());

            if (received.equals("end")) {
                running = false;
                continue;
            }
            socket.send(packet);
        }
        socket.close();
    }
}

Мы создаем глобальныйDatagramSocket, который мы будем использовать повсюду для отправки пакетов, байтовый массив для упаковки наших сообщений и переменную состояния с именемrunning.

Для простоты сервер расширяетThread, поэтому мы можем реализовать все внутри методаrun.

Внутриrun мы создаем цикл while, который выполняется до тех пор, покаrunning не изменится на false из-за некоторой ошибки или сообщения о завершении от клиента.

В верхней части цикла мы создаем экземплярDatagramPacket для приема входящих сообщений.

Затем мы вызываем методreceive для сокета. Этот метод блокируется до тех пор, пока не поступит сообщение, и сохраняет сообщение внутри байтового массива переданных емуDatagramPacket.

После получения сообщения мы получаем адрес и порт клиента, так как собираемся отправить ответ обратно.

Затем мы создаемDatagramPacket для отправки сообщения клиенту. Обратите внимание на разницу в подписи с принимающим пакетом. Для этого также требуется адрес и порт клиента, которому мы отправляем сообщение.

5. Клиент

Теперь давайте развернем простой клиент для этого нового сервера:

public class EchoClient {
    private DatagramSocket socket;
    private InetAddress address;

    private byte[] buf;

    public EchoClient() {
        socket = new DatagramSocket();
        address = InetAddress.getByName("localhost");
    }

    public String sendEcho(String msg) {
        buf = msg.getBytes();
        DatagramPacket packet
          = new DatagramPacket(buf, buf.length, address, 4445);
        socket.send(packet);
        packet = new DatagramPacket(buf, buf.length);
        socket.receive(packet);
        String received = new String(
          packet.getData(), 0, packet.getLength());
        return received;
    }

    public void close() {
        socket.close();
    }
}

Код не сильно отличается от кода сервера. У нас есть глобальныйDatagramSocket и адрес сервера. Мы создаем их в конструкторе.

У нас есть отдельный метод, который отправляет сообщения на сервер и возвращает ответ.

Сначала мы конвертируем строковое сообщение в массив байтов, а затем создаемDatagramPacket для отправки сообщений.

Далее - отправляем сообщение. Сразу конвертируемDatagramPacket в принимающий.

Когда приходит эхо, мы конвертируем байты в строку и возвращаем строку.

6. Тест

В классеUDPTest.java мы просто создаем один тест для проверки способности двух наших приложений отражать эхо:

public class UDPTest {
    EchoClient client;

    @Before
    public void setup(){
        new EchoServer().start();
        client = new EchoClient();
    }

    @Test
    public void whenCanSendAndReceivePacket_thenCorrect() {
        String echo = client.sendEcho("hello server");
        assertEquals("hello server", echo);
        echo = client.sendEcho("server is working");
        assertFalse(echo.equals("hello server"));
    }

    @After
    public void tearDown() {
        client.sendEcho("end");
        client.close();
    }
}

Вsetup мы запускаем сервер, а также создаем клиента. Находясь в методеtearDown, мы отправляем сообщение о завершении на сервер, чтобы он мог закрыть, и в то же время мы закрываем клиент.

7. Заключение

В этой статье мы узнали о протоколе пользовательских дейтаграмм и успешно создали собственные клиент-серверные приложения, которые взаимодействуют через UDP.

Чтобы получить полный исходный код примеров, использованных в этой статье, вы можете ознакомиться с файломGitHub project.