介绍
本文是“Java Socket编程实例”系列的第五篇,将介绍Java NIO中的UDP(User Datagram Protocol)实践。UDP是一种面向无连接的协议,常用于高速传输数据、广播和流媒体等场景。相比于TCP,UDP的特点是传输速度快、没有连接建立和断开的过程,但是可靠性差,无法保证数据传输的顺序和正确性。
在本文中,我们将使用Java NIO中的DatagramChannel和Selector实现UDP的发送和接收,并实现一个简单的聊天程序。
UDP发送
DatagramChannel是Java NIO中专门用于UDP的通道,在使用前需要先打开通道。通道打开后,可以创建DatagramPacket(要发送的数据包)和SocketAddress(目标地址),将数据包发送给目标地址。
下面是一个简单的UDP发送示例:
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
public class UdpSender {
public static void main(String[] args) throws Exception {
DatagramChannel channel = DatagramChannel.open();
String message = "Hello, UDP!";
ByteBuffer buffer = ByteBuffer.wrap(message.getBytes());
InetSocketAddress address = new InetSocketAddress("localhost", 8888);
channel.send(buffer, address);
channel.close();
}
}
上面代码中,我们打开一个DatagramChannel,创建一个ByteBuffer,包装要发送的消息,创建要发送的SocketAddress,将数据包发送给目标地址。
注意,DatagramChannel中的send方法是非阻塞的,如果目标地址不可用,则立即返回IOException。如果需要进行重试或者报错处理,需要通过try-catch块捕获IOException并进行处理。
UDP接收
同样地,DatagramChannel也可以用来接收UDP数据包。接收数据包需要用到Selector进行多路复用,使用Selector监听DatagramChannel的可读事件,当有事件发生时处理数据包。
下面是UDP接收的示例:
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.Selector;
import java.nio.channels.SelectionKey;
public class UdpReceiver {
public static void main(String[] args) throws Exception {
DatagramChannel channel = DatagramChannel.open();
channel.bind(new InetSocketAddress(8888));
channel.configureBlocking(false);
Selector selector = Selector.open();
channel.register(selector, SelectionKey.OP_READ);
while (true) {
selector.select();
for (SelectionKey key : selector.selectedKeys()) {
if (key.isReadable()) {
ByteBuffer buffer = ByteBuffer.allocate(1024);
InetSocketAddress address = (InetSocketAddress) channel.receive(buffer);
if (address != null) {
buffer.flip();
String message = new String(buffer.array(), 0, buffer.limit());
System.out.println(address + ": " + message);
}
}
}
}
}
}
上面的代码块中,我们首先打开一个DatagramChannel,并绑定端口号。绑定端口号之后需要将通道设置为非阻塞模式,并将通道注册到Selector中并监听可读事件。
在while循环中,使用Selector.select()等待事件的发生。当可读事件发生时,使用DatagramChannel的receive方法接收数据包,并将数据包中的数据读入到ByteBuffer的缓冲区中。读取完数据之后,我们需要将Buffer切换到读模式,解析缓冲区中的消息内容,然后进行下一步的操作。
异步定时器
我们可以使用Java NIO自带的异步定时器来控制发送和接收消息的时间。
下面是定时器的示例:
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.Selector;
import java.nio.channels.SelectionKey;
public class UdpTimer {
public static void main(String[] args) throws Exception {
DatagramChannel channel = DatagramChannel.open();
channel.bind(null);
channel.configureBlocking(false);
Selector selector = Selector.open();
SelectionKey writeKey = channel.register(selector, SelectionKey.OP_WRITE);
SelectionKey readKey = channel.register(selector, SelectionKey.OP_READ);
ByteBuffer buffer = ByteBuffer.wrap("Ping".getBytes());
while (true) {
int readyChannels = selector.select();
if (readyChannels == 0) continue;
for (SelectionKey key : selector.selectedKeys()) {
if (key.isWritable()) {
channel.send(buffer, readKey.attachment());
writeKey.interestOps(0);
readKey.interestOps(SelectionKey.OP_READ);
} else if (key.isReadable()) {
ByteBuffer response = ByteBuffer.allocate(1024);
channel.receive(response);
System.out.println(new String(response.array(), 0, response.limit()));
readKey.interestOps(0);
writeKey.interestOps(SelectionKey.OP_WRITE);
}
}
}
}
}
在上面的代码中,我们首先创建一个无需绑定端口号的DatagramChannel,创建一个ByteBuffer,并将ByteBuffer中的数据填充为Ping请求。接着注册事件并等待事件发生。如果事件发生了,则进入while循环并根据事件类型进行相应的处理。
当写事件发生时,该程序发送Ping请求并关闭写事件监控,开始等待读事件发生。当读事件发生时,该程序从DatagramChannel中读取并解析回应,并关闭读事件监控,重新开始监控写事件。
使用以上方法可以实现UDP的异步通信。
总结
本文介绍了Java NIO中UDP的基本使用。通过使用DatagramChannel和Selector,我们可以实现UDP的发送和接收,并实现一个简单的聊天程序。此外,本文还介绍了Java NIO自带的异步定时器,可以用来控制发送和接收消息的时间。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java Socket编程实例(五)- NIO UDP实践 - Python技术站