Перейти к основному содержимому

Канал дейтаграмм Java NIO

· 5 мин. чтения

1. Обзор

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

2. Датаграммный канал

Среди различных протоколов, поддерживаемых в Интернете, TCP и UDP являются наиболее распространенными.

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

Класс DatagramChannel модуля Java NIO предоставляет выбираемый канал для сокетов, ориентированных на дейтаграммы . Другими словами, он позволяет создать канал дейтаграмм для отправки и получения дейтаграмм (пакетов UDP).

Давайте воспользуемся классом DatagramChannel для создания клиента, который отправляет дейтаграммы по локальному IP-адресу, и сервера, принимающего дейтаграммы.

3. Открыть и связать

Во-первых, давайте создадим класс DatagramChannelBuilder с методом openChannel , который предоставляет открытый, но неподключенный канал дейтаграмм:

public class DatagramChannelBuilder {
public static DatagramChannel openChannel() throws IOException {
DatagramChannel datagramChannel = DatagramChannel.open();
return datagramChannel;
}
}

Затем нам потребуется привязать открытый канал к локальному адресу для прослушивания входящих UDP-пакетов.

Итак, мы добавим метод bindChannel , который привязывает DatagramChannel к предоставленному локальному адресу:

public static DatagramChannel bindChannel(SocketAddress local) throws IOException {
return openChannel().bind(local);
}

Теперь мы можем использовать класс DatagramChannelBuilder для создания клиента/сервера, который отправляет/получает UDP-пакеты по настроенному адресу сокета.

4. Клиент

Во-первых, давайте создадим класс DatagramClient с методом startClient , который использует уже рассмотренный метод bindChannel класса DatagramChannelBuilder : ``

public class DatagramClient {
public static DatagramChannel startClient() throws IOException {
DatagramChannel client = DatagramChannelBuilder.bindChannel(null);
return client;
}
}

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

Затем добавим метод sendMessage для отправки дейтаграммы на адрес сервера:

public static void sendMessage(DatagramChannel client, String msg, SocketAddress serverAddress) throws IOException {
ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
client.send(buffer, serverAddress);
}

Вот и все! Теперь мы готовы отправить наше сообщение с помощью клиента:

DatagramChannel client = startClient();
String msg = "Hello, this is a ForEach's DatagramChannel based UDP client!";
InetSocketAddress serverAddress = new InetSocketAddress("localhost", 7001);

sendMessage(client, msg, serverAddress);

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

5. Сервер

Точно так же создадим класс DatagramServer с методом startServer для запуска сервера по адресу localhost:7001 :

public class DatagramServer {
public static DatagramChannel startServer() throws IOException {
InetSocketAddress address = new InetSocketAddress("localhost", 7001);
DatagramChannel server = DatagramChannelBuilder.bindChannel(address);
System.out.println("Server started at #" + address);
return server;
}
}

Затем добавим метод receiveMessage , который получает дейтаграммы от клиента, извлекает сообщение и печатает его:

public static void receiveMessage(DatagramChannel server) throws IOException {
ByteBuffer buffer = ByteBuffer.allocate(1024);
SocketAddress remoteAdd = server.receive(buffer);
String message = extractMessage(buffer);
System.out.println("Client at #" + remoteAdd + " sent: " + message);
}

Также для извлечения сообщения клиента из полученного буфера нам потребуется добавить метод extractMessage :

private static String extractMessage(ByteBuffer buffer) {
buffer.flip();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
String msg = new String(bytes);

return msg;
}

Здесь мы использовали метод flip для экземпляра ByteBuffer , чтобы переключиться с чтения из ввода-вывода на запись в ввод-вывод. Кроме того, метод flip устанавливает ограничение на текущую позицию и позицию на ноль, чтобы мы могли читать с самого начала.

Теперь мы можем запустить наш сервер и получить сообщение от клиента:

DatagramChannel server = startServer();
receiveMessage(server);

Следовательно, вывод на печать, когда сервер получит наше сообщение, будет таким:

Server started at #localhost/127.0.0.1:7001
Client at #/127.0.0.1:52580 sent: Hello, this is a ForEach's DatagramChannel based UDP client!

6. DatagramChannelUnitTest

Теперь, когда у нас есть и клиент, и сервер, мы можем написать модульный тест для проверки сквозной доставки дейтаграммы (пакета UDP):

@Test
public void whenClientSendsAndServerReceivesUDPPacket_thenCorrect() throws IOException {
DatagramChannel server = DatagramServer.startServer();
DatagramChannel client = DatagramClient.startClient();
String msg1 = "Hello, this is a ForEach's DatagramChannel based UDP client!";
String msg2 = "Hi again!, Are you there!";
InetSocketAddress serverAddress = new InetSocketAddress("localhost", 7001);

DatagramClient.sendMessage(client, msg1, serverAddress);
DatagramClient.sendMessage(client, msg2, serverAddress);

assertEquals("Hello, this is a ForEach's DatagramChannel based UDP client!", DatagramServer.receiveMessage(server));
assertEquals("Hi again!, Are you there!", DatagramServer.receiveMessage(server));
}

Во-первых, мы запустили сервер, который связывает канал дейтаграмм для прослушивания входящего сообщения на localhost:7001 . Затем мы запустили клиент и отправили два сообщения.

Наконец, мы получили входящие сообщения на сервер и сравнили их с сообщениями, отправленными через клиент.

7. Дополнительные методы

До сих пор мы использовали такие методы, как open , bind , send и receive , предоставляемые классом DatagramChannel . Теперь давайте быстро пройдемся по другим удобным методам.

7.1. настроить блокировку

По умолчанию канал дейтаграмм блокируется. Мы можем использовать метод configureBlocking , чтобы сделать канал неблокирующим при передаче ложного значения:

client.configureBlocking(false);

7.2. подключен

Метод isConnected возвращает состояние канала дейтаграммы, то есть, подключен он или нет.

7.3. разъем

Метод socket возвращает объект класса DatagramSocket , связанный с каналом дейтаграммы.

7.4. Закрыть

Кроме того, мы можем закрыть канал, вызвав метод close класса DatagramChannel .

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

В этом кратком руководстве мы рассмотрели класс DatagramChannel Java NIO, который позволяет создавать канал дейтаграмм для отправки/получения пакетов UDP.

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

Затем мы создали клиент и сервер для изучения сквозной доставки пакетов UDP с помощью класса DatagramChannel .

Как обычно, исходный код доступен на GitHub .