JAVA异步的UDP 通讯-客户端
1. 使用DatagramSocket
的非阻塞模式
Java的DatagramSocket
默认是阻塞模式,但可以通过设置Socket
选项来启用非阻塞模式。这样可以在发送和接收数据时避免线程阻塞
import java.net.DatagramSocket;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.StandardSocketOptions;
public class AsyncUDPClient {
public static void main(String[] args) throws Exception {
DatagramSocket socket = DatagramSocket.create();
socket.setOption(StandardSocketOptions.SO_REUSEADDR, true); // 允许端口复用
socket.connect(InetAddress.getByName("localhost"), 12345); // 连接到服务器
socket.setSoTimeout(1000); // 设置超时时间
// 发送数据
String message = "Hello, UDP Server!";
byte[] data = message.getBytes();
DatagramPacket packet = new DatagramPacket(data, data.length);
socket.send(packet);
// 接收响应
byte[] receiveBuffer = new byte[1024];
DatagramPacket receivePacket = new DatagramPacket(receiveBuffer, receiveBuffer.length);
socket.receive(receivePacket);
String response = new String(receivePacket.getData(), 0, receivePacket.getLength());
System.out.println("Received from server: " + response);
socket.close();
}
}
2. 使用线程池实现异步处理
通过线程池来处理UDP数据的发送和接收,可以避免阻塞主线程,提高程序的响应性。
import java.net.DatagramSocket;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class AsyncUDPClientWithThreadPool {
private static final ExecutorService executor = Executors.newFixedThreadPool(4);
public static void main(String[] args) throws Exception {
DatagramSocket socket = new DatagramSocket();
socket.connect(InetAddress.getByName("localhost"), 12345);
// 异步发送数据
executor.submit(() -> {
try {
String message = "Hello, UDP Server!";
byte[] data = message.getBytes();
DatagramPacket packet = new DatagramPacket(data, data.length);
socket.send(packet);
System.out.println("Message sent to server.");
} catch (Exception e) {
e.printStackTrace();
}
});
// 异步接收数据
executor.submit(() -> {
try {
byte[] receiveBuffer = new byte[1024];
DatagramPacket receivePacket = new DatagramPacket(receiveBuffer, receiveBuffer.length);
socket.receive(receivePacket);
String response = new String(receivePacket.getData(), 0, receivePacket.getLength());
System.out.println("Received from server: " + response);
} catch (Exception e) {
e.printStackTrace();
}
});
executor.shutdown();
socket.close();
}
}
3. 使用Selector
实现多路复用
Selector
可以用于同时监控多个DatagramSocket
,从而实现更高效的异步通信。
import java.net.DatagramSocket;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.ByteBuffer;
public class AsyncUDPClientWithSelector {
public static void main(String[] args) throws Exception {
DatagramChannel channel = DatagramChannel.open();
channel.configureBlocking(false);
channel.connect(InetAddress.getByName("localhost"), 12345);
Selector selector = Selector.open();
channel.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ);
while (selector.select() > 0) {
for (SelectionKey key : selector.selectedKeys()) {
if (key.isWritable()) {
String message = "Hello, UDP Server!";
byte[] data = message.getBytes();
DatagramPacket packet = new DatagramPacket(data, data.length);
channel.send(ByteBuffer.wrap(data), channel.socket().getRemoteSocketAddress());
System.out.println("Message sent to server.");
}
if (key.isReadable()) {
byte[] receiveBuffer = new byte[1024];
DatagramPacket receivePacket = new DatagramPacket(receiveBuffer, receiveBuffer.length);
channel.receive(receivePacket);
String response = new String(receivePacket.getData(), 0, receivePacket.getLength());
System.out.println("Received from server: " + response);
}
selector.selectedKeys().remove(key);
}
}
channel.close();
selector.close();
}
}
4. 设置合理的超时和缓冲区大小
通过设置setSoTimeout
和setReceiveBufferSize
等参数,可以优化UDP客户端的性能。
socket.setSoTimeout(1000); // 设置接收超时时间
socket.setReceiveBufferSize(8192); // 设置接收缓冲区大小
socket.setSendBufferSize(8192); // 设置发送缓冲区大小