概述
实现一个UDP代理服务器,它监听一个指定的端口,将接收到的UDP数据包转发到目标主机,并将目标主机的响应转发回原始客户端。使用线程池来异步处理响应,并使用日志记录器来记录不同级别的日志信息。
源代码
java
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
public class UDPProxy {
private final int listenPort;
private final String targetHost;
private final int targetPort;
private final Map<DatagramSocket, DatagramPacket> downPacketCache = new ConcurrentHashMap<>();
private final ThreadPoolExecutor executor = new ThreadPoolExecutor(8, 16,
60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(200), new ThreadPoolExecutor.CallerRunsPolicy());
private static final Logger logger = Logger.getLogger("UDPProxy");
public UDPProxy(int listenPort, String targetHost, int targetPort) {
this.listenPort = listenPort;
this.targetHost = targetHost;
this.targetPort = targetPort;
//设置日志级别
logger.setLevel(Level.INFO);
}
public void start() {
try (DatagramSocket thisSocket = new DatagramSocket(listenPort)) {
byte[] receiveData = new byte[4096];
logger.warning("UDP Proxy started, listening on port " + listenPort);
Map<InetAddress, DatagramSocket> upSocketCache = new ConcurrentHashMap<>();
while (true) {
DatagramPacket upPacket = new DatagramPacket(receiveData, receiveData.length);
thisSocket.receive(upPacket);
byte[] data = upPacket.getData();
int length = upPacket.getLength();
InetAddress clientAddress = upPacket.getAddress();
logger.warning("[UP] Size:" + length + ",from" + clientAddress + ":" + upPacket.getPort());
logger.info("[UP] Packet:" + new String(data, 0, length, StandardCharsets.UTF_8));
// Forward the packet to the target host
InetAddress targetAddress = InetAddress.getByName(targetHost);
DatagramPacket sendPacket = new DatagramPacket(data, length, targetAddress, targetPort);
DatagramSocket upSocket = upSocketCache.get(clientAddress);
if (upSocket == null) {
upSocket = new DatagramSocket();
upSocketCache.put(clientAddress, upSocket);
downPacketCache.put(upSocket, upPacket);
upSocket.send(sendPacket);
responseListener(thisSocket, upSocket);
} else {
upSocket.send(sendPacket);
}
}
} catch (IOException e) {
logger.log(Level.SEVERE, "Error in UDPProxy", e);
}
}
private void responseListener(DatagramSocket thisSocket, DatagramSocket upSocket) {
executor.execute(() -> {
while (true) {
byte[] downData = new byte[4096];
DatagramPacket downPacket = new DatagramPacket(downData, downData.length);
byte[] data = downPacket.getData();
InetAddress downPacketAddress;
try {
upSocket.receive(downPacket);
downPacketAddress = downPacket.getAddress();
int length = downPacket.getLength();
logger.warning("[DOWN] Size:" + length + ",From" + downPacketAddress + ":" + downPacket.getPort());
logger.info("[DOWN] Packet:"+ new String(data, 0, length, StandardCharsets.UTF_8));
DatagramPacket datagramPacket = downPacketCache.get(upSocket);
if (datagramPacket != null) {
datagramPacket.setData(data);
thisSocket.send(datagramPacket);
}
} catch (IOException e) {
logger.log(Level.SEVERE, "Error in responseListener", e);
}
}
});
}
public static void main(String[] args) {
if (args.length != 3) {
logger.warning("Usage: java UDPProxy <listenPort> <targetHost> <targetPort>");
System.exit(1);
}
int listenPort = Integer.parseInt(args[0]);
String targetHost = args[1];
int targetPort = Integer.parseInt(args[2]);
UDPProxy proxy = new UDPProxy(listenPort, targetHost, targetPort);
proxy.start();
}
}
代码启动
java UDPProxy <listenPort> <targetHost> <targetPort>
-
listenPort
:代理服务器监听的端口。 -
targetHost
:目标主机的地址。 -
targetPort
:目标主机的端口。
bash
java UDPProxy 12681 192.168.18.76 6666
代码剖析
类和成员变量
-
成员变量:
-
listenPort
:代理服务器监听的端口。 -
targetHost
:目标主机的地址。 -
targetPort
:目标主机的端口。 -
downPacketCache
:用于缓存下行数据包的映射。 -
executor
:用于处理响应监听的线程池。 -
logger
:用于日志记录的Logger对象。
-
构造方法
-
构造方法:
-
初始化
listenPort
、targetHost
和targetPort
。 -
设置日志级别为
INFO
。
-
start方法
-
start方法:
-
创建一个
DatagramSocket
实例,监听指定的端口。 -
无限循环接收传入的UDP数据包。
-
将接收到的数据包转发到目标主机。
-
使用
upSocketCache
缓存每个客户端的DatagramSocket
实例。 -
调用
responseListener
方法监听目标主机的响应。
-
responseListener方法
-
responseListener方法:
-
使用线程池异步处理目标主机的响应。
-
接收目标主机的响应数据包。
-
从
downPacketCache
中获取原始客户端的数据包,并将响应数据包发送回原始客户端。
-
main方法
-
main方法:
- 解析命令行参数,创建
UDPProxy
实例并启动代理服务器。
- 解析命令行参数,创建