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 .