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

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

Он называется TCP (Transfer Control Protocol). В противоположность этому, другой способ имеет очень мало правил, и поэтому он ускоряет общение, но не гарантирует точность. Он называется UDP (User Datagram Protocol). Если вам будет дан шанс выбрать один из них, какой вы выберете?

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

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

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

Но почему именно UDP?

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

Неспособность поставлять контент в режиме реального времени может критически сказаться на росте поставщика потоковой передачи и его позиции на рынке.

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

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

  1. socket() — прежде всего сокет определяется как для сервера, так и для клиента. Это не обязательно должно происходить одновременно. Чтобы объяснение было исчерпывающим, я буду на каждом этапе обсуждать действия как сервера, так и клиента.
  2. bind() — сокету, который определен, присваивается идентификатор и порт на работающей машине. Для клиентского сокета это необязательно, потому что даже если клиентский сокет не привязан, привязка осуществляется автоматически всякий раз, когда клиент инициирует подключение к серверу.
  3. recvfrom() — после привязки к порту компьютера серверный сокет ожидает подключения от клиентского сокета. Тем временем дальнейшее выполнение текущего потока останавливается (блокируется) до тех пор, пока серверный сокет не получит соединение. То же самое происходит и с клиентским сокетом при ожидании ответа сервера.
  4. sendto() — после соединения с клиентом серверный сокет отправляет данные клиенту. Этот же метод используется клиентским сокетом для выполнения запроса на подключение к серверу.
  5. close() — после успешного обмена данными оба сокета закрываются, т.е. освобождаются ресурсы системы, выделенные для сокетов.

Коммуникация UPD-сокетов в действии

Классы DatagramPacket и DatagramSocket в Java поддерживают использование коммуникации UDP-сокетов на уровне приложения. Давайте напишем простой сервер и клиент на Java, которые взаимодействуют друг с другом через UDP-сокеты.

В этом примере серверный сокет получает данные типа String от клиента и отправляет заглавную строку обратно клиенту.

Поскольку мы уже осведомлены об этапах коммуникации UDP-сокетов, я поместил комментарии в обе Java-программы, чтобы объяснить код, а не использовать длинные абзацы. Этот способ также поможет вам быстрее понять код, пока вы будете его читать.

Построение серверного UDP-сокета

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;

public class UDPServer{
  // Серверный UDP-сокет запущен на этом порту
  public final static int SERVICE_PORT=50001;
 
  public static void main(String[] args) throws IOException{
    try{
      // Создайте новый экземпляр DatagramSocket, чтобы получать ответы от клиента
      DatagramSocket serverSocket = new DatagramSocket(SERVICE_PORT);
      
      /* Создайте буферы для хранения отправляемых и получаемых данных.
Они временно хранят данные в случае задержек связи */
      byte[] receivingDataBuffer = new byte[1024];
      byte[] sendingDataBuffer = new byte[1024];
      
      /* Создайте экземпляр UDP-пакета для хранения клиентских данных с использованием буфера для полученных данных */
      DatagramPacket inputPacket = new DatagramPacket(receivingDataBuffer, receivingDataBuffer.length);
      System.out.println("Waiting for a client to connect...");
      
      // Получите данные от клиента и сохраните их в inputPacket
      serverSocket.receive(inputPacket);
      
      // Выведите на экран отправленные клиентом данные
      String receivedData = new String(inputPacket.getData());
      System.out.println("Sent from the client: "+receivedData);
      
      /* 
      * Преобразуйте отправленные клиентом данные в верхний регистр, 
      * Преобразуйте их в байты
      * и сохраните в соответствующий буфер. */
      sendingDataBuffer = receivedData.toUpperCase().getBytes();
      
      // Получите IP-адрес и порт клиента
      InetAddress senderAddress = inputPacket.getAddress();
      int senderPort = inputPacket.getPort();
      
      // Создайте новый UDP-пакет с данными, чтобы отправить их клиенту
      DatagramPacket outputPacket = new DatagramPacket(
        sendingDataBuffer, sendingDataBuffer.length,
        senderAddress,senderPort
      );
      
      // Отправьте пакет клиенту
      serverSocket.send(outputPacket);
      // Закройте соединение сокетов
      serverSocket.close();
    }
    catch (SocketException e){
      e.printStackTrace();
    }
  }
}

Создание клиентского UDP-сокета

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;

public class UDPClient{
  /* Порт сервера, к которому собирается
подключиться клиентский сокет */
  public final static int SERVICE_PORT = 50001;
  
  public static void main(String[] args) throws IOException{
    try{
      /* Создайте экземпляр клиентского сокета.
Нет необходимости в привязке к определенному порту */
      DatagramSocket clientSocket = new DatagramSocket();
      
      // Получите IP-адрес сервера
      InetAddress IPAddress = InetAddress.getByName("localhost");
      
      // Создайте соответствующие буферы
      byte[] sendingDataBuffer = new byte[1024];
      byte[] receivingDataBuffer = new byte[1024];
      
      /* Преобразуйте данные в байты 
       и разместите в буферах */
      String sentence = "Hello from UDP client";
      sendingDataBuffer = sentence.getBytes();
      
      // Создайте UDP-пакет
      DatagramPacket sendingPacket = new DatagramPacket(sendingDataBuffer,sendingDataBuffer.length,IPAddress, SERVICE_PORT);
      
      // Отправьте UDP-пакет серверу
      clientSocket.send(sendingPacket);
      
      // Получите ответ от сервера, т.е. предложение из заглавных букв
      DatagramPacket receivingPacket = new DatagramPacket(receivingDataBuffer,receivingDataBuffer.length);
      clientSocket.receive(receivingPacket);
      
      // Выведите на экране полученные данные
      String receivedData = new String(receivingPacket.getData());
      System.out.println("Sent from the server: "+receivedData);
      
      // Закройте соединение с сервером через сокет
      clientSocket.close();
    }
    catch(SocketException e) {
      e.printStackTrace();
    }
  }
}

Запуск программ: демонстрация работы сокетов

Поскольку мы уже создали и сервер, и клиента, давайте запустим обе программы и посмотрим их в действии. Сначала запустите программу UDPServer.java, а затем UDPClient.java.

Вы увидите на консоли/терминале вывод каждой программы, как это показано ниже. Это гарантирует, что между нашими сервером и клиентом произошла успешная коммуникация сокетов.

UDPServer.java

Waiting for a client to connect...
Sent from the client: Hello from UDP client

UDPClient.java

sent from the server: HELLO FROM UDP CLIENT

Вот наше руководство и подошло к концу. Из этой статьи мы узнали, что такое коммуникация UDP-сокетов и как ее продемонстрировать на Java. Я искренне надеюсь, что информация вам пригодится.

Спасибо за чтение!

Читайте также:


Перевод статьи: Pavindu Lakshan, “Fundamentals of UDP Socket Programming in Java”

Предыдущая статьяРуководство разработчика по оптимизации скорости работы веб-сайтов
Следующая статьяПрекращайте пользоваться Git CLI