解决通过mc基岩 udp代理,同一个代理端口只能有一个人连接的问题

This commit is contained in:
aoshiguchen
2025-11-23 13:06:30 +08:00
parent 9cbcf4de2e
commit 150c69d37c
5 changed files with 87 additions and 30 deletions
@@ -59,6 +59,13 @@ public class ProxyTunnelChannelHandler extends SimpleChannelInboundHandler<Proxy
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
{
// udp情况下不能使用visitorChannel上的visitorId,因为所有的用户都是走的同一个VisitorChannel
String visitorId = ctx.channel().attr(Constants.VISITOR_ID).get();
// 删除Udp的SocketAddress
ProxyUtil.removeSocketAddressByVisitorId(visitorId);
}
Channel visitorChannel = ctx.channel().attr(Constants.NEXT_CHANNEL).get();
if (null != visitorChannel) {
Integer licenseId = ctx.channel().attr(Constants.LICENSE_ID).get();
@@ -35,7 +35,12 @@ public class UdpVisitorChannelHandler extends SimpleChannelInboundHandler<Datagr
datagramPacket.content().readBytes(bytes);
datagramPacket.content().resetReaderIndex();
ProxyAttachment proxyAttachment = new ProxyAttachment(ctx.channel(), bytes, (channel, buf) -> {
Channel proxyChannel = channel.attr(Constants.NEXT_CHANNEL).get();
String visitorId = ProxyUtil.getVisitorIdBySocketAddress(datagramPacket.sender());
if (StrUtil.isBlank(visitorId)) {
return;
}
Channel proxyChannel = ProxyUtil.getTunnelChannelByVisitorId(visitorId);
// Channel proxyChannel = channel.attr(Constants.NEXT_CHANNEL).get();
if (null == proxyChannel) {
// // 该端口还没有代理客户端
@@ -50,7 +55,6 @@ public class UdpVisitorChannelHandler extends SimpleChannelInboundHandler<Datagr
Long proxyTimeoutMs = proxyChannel.attr(Constants.PROXY_TIMEOUT_MS).get();
// 转发代理数据
String visitorId = ProxyUtil.getVisitorIdByChannel(channel);
proxyChannel.writeAndFlush(ProxyMessage.buildUdpTransferMessage(new ProxyMessage.UdpBaseInfo()
.setVisitorId(visitorId)
.setVisitorIp(datagramPacket.sender().getAddress().getHostAddress())
@@ -66,8 +70,9 @@ public class UdpVisitorChannelHandler extends SimpleChannelInboundHandler<Datagr
Solon.context().getBean(FlowReportService.class).addWriteByte(visitorChannelAttachInfo.getLicenseId(), bytes.length);
});
Channel proxyChannel = ctx.channel().attr(Constants.NEXT_CHANNEL).get();
if (null != proxyChannel && proxyChannel.isActive()) {
String visitorId = ProxyUtil.getVisitorIdBySocketAddress(datagramPacket.sender());
Channel proxyChannel = ProxyUtil.getTunnelChannelByVisitorId(visitorId);
if (StrUtil.isNotBlank(visitorId) && null != proxyChannel && proxyChannel.isActive()) {
// UDP代理隧道已就绪,直接转发
proxyAttachment.execute();
return;
@@ -96,8 +101,10 @@ public class UdpVisitorChannelHandler extends SimpleChannelInboundHandler<Datagr
// // 用户连接到代理服务器时,设置用户连接不可读,等待代理后端服务器连接成功后再改变为可读状态
// visitorChannel.config().setOption(ChannelOption.AUTO_READ, false);
// TODO UDP此处叫visitor似有不妥,与TCP不同,2.x重构思考
String visitorId = ProxyUtil.newVisitorId();
// UDP场景下,一个visitorId并不对应一个visitorChannel,所有visitor共用一个visitorChannel
visitorId = ProxyUtil.newVisitorId();
// 设置udp发送方的地址
ProxyUtil.setVisitorIdToSocketAddressMap(visitorId, datagramPacket.sender());
// 此处需要和tcp分开
ProxyUtil.addVisitorChannelToCmdChannel(NetworkProtocolEnum.UDP, cmdChannel, visitorId, visitorChannel, sa.getPort());
ProxyUtil.addProxyConnectAttachment(visitorId, proxyAttachment);
@@ -87,7 +87,9 @@ public class UdpProxyMessageConnectHandler implements ProxyMessageHandler {
ctx.channel().attr(Constants.TARGET_PORT).set(portMappingDO.getClientPort());
ctx.channel().attr(Constants.PROXY_RESPONSES).set(portMappingDO.getProxyResponses());
ctx.channel().attr(Constants.PROXY_TIMEOUT_MS).set(portMappingDO.getProxyTimeoutMs());
visitorChannel.attr(Constants.NEXT_CHANNEL).set(ctx.channel());
// 所有udb visitor共用一个visitorChannel,设置这个没啥用
// visitorChannel.attr(Constants.NEXT_CHANNEL).set(ctx.channel());
ProxyUtil.setVisitorIdToTunnelChannelMap(udpBaseInfo.getVisitorId(), ctx.channel());
visitorChannel.attr(Constants.IS_UDP_KEY).set(Boolean.TRUE);
// // 代理客户端与后端服务器连接成功,修改用户连接为可读状态
// visitorChannel.config().setOption(ChannelOption.AUTO_READ, true);
@@ -31,33 +31,31 @@ public class UdpProxyMessageTransferHandler implements ProxyMessageHandler {
log.debug("[UDP transfer]info:{} data:{}", proxyMessage.getInfo(), new String(proxyMessage.getData()));
Channel visitorChannel = ctx.channel().attr(Constants.NEXT_CHANNEL).get();
if (null != visitorChannel) {
if (null == visitorChannel) {
return;
}
if (!visitorChannel.isWritable()) {
//自己不可写,通道可以读,让通道关闭读
//自己可写,通道不可以读,让通道打开读
if (ctx.channel().config().isAutoRead()) {
ctx.channel().config().setAutoRead(false);
}
} else {
if (ctx.channel().config().isAutoRead()) {
ctx.channel().config().setAutoRead(true);
}
if (!visitorChannel.isWritable()) {
//自己不可写,通道可以读,让通道关闭读
//自己可写,通道不可以读,让通道打开读
if (ctx.channel().config().isAutoRead()) {
ctx.channel().config().setAutoRead(false);
}
} else {
if (ctx.channel().config().isAutoRead()) {
ctx.channel().config().setAutoRead(true);
}
}
// InetSocketAddress address = new InetSocketAddress(udpBaseInfo.getVisitorIp(), udpBaseInfo.getVisitorPort());
// ByteBuf byteBuf = Unpooled.copiedBuffer(proxyMessage.getData());
// visitorChannel.writeAndFlush(new DatagramPacket(byteBuf, address));
InetSocketAddress address = ctx.channel().attr(Constants.SENDER).get();
if (null != address) {
visitorChannel.writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer(proxyMessage.getData()), address));
}
InetSocketAddress address = ctx.channel().attr(Constants.SENDER).get();
if (null != address) {
visitorChannel.writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer(proxyMessage.getData()), address));
}
// 增加流量计数(TODO 如果UDP映射服务端端口修改,这个似乎不准)
Integer licenseId = visitorChannel.attr(Constants.LICENSE_ID).get();
if (null != licenseId) {
Solon.context().getBean(FlowReportService.class).addReadByte(licenseId, proxyMessage.getData().length);
}
// 增加流量计数(TODO 如果UDP映射服务端端口修改,这个似乎不准)
Integer licenseId = visitorChannel.attr(Constants.LICENSE_ID).get();
if (null != licenseId) {
Solon.context().getBean(FlowReportService.class).addReadByte(licenseId, proxyMessage.getData().length);
}
}
@@ -13,6 +13,7 @@ import io.netty.channel.Channel;
import io.netty.util.AttributeKey;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
@@ -72,6 +73,18 @@ public class ProxyUtil {
* licenseId - 客户端Id映射
*/
private static Map<Integer, String> licenseIdToClientIdMap = new HashMap<>();
/**
* visitorId - SocketAddress映射
*/
private static Map<String, SocketAddress> visitorIdToSocketAddressMap = new HashMap<>();
/**
* SocketAddress - visitorId 映射
*/
private static Map<SocketAddress, String> socketAddressToVisitorIdMap = new HashMap<>();
/**
* visitorId - tunnelChannel 映射
*/
private static Map<String, Channel> visitorIdToTunnelChannelMap = new HashMap<>();
/**
* 初始化代理信息
@@ -479,4 +492,34 @@ public class ProxyUtil {
public static void removeClientIdByLicenseId(Integer licenseId) {
licenseIdToClientIdMap.remove(licenseId);
}
public static void setVisitorIdToSocketAddressMap(String visitorId, SocketAddress socketAddress) {
visitorIdToSocketAddressMap.put(visitorId, socketAddress);
socketAddressToVisitorIdMap.put(socketAddress, visitorId);
}
public static SocketAddress getSocketAddressByVisitorId(String visitorId) {
return visitorIdToSocketAddressMap.get(visitorId);
}
public static void removeSocketAddressByVisitorId(String visitorId) {
SocketAddress socketAddress = visitorIdToSocketAddressMap.get(visitorId);
visitorIdToSocketAddressMap.remove(visitorId);
socketAddressToVisitorIdMap.remove(socketAddress);
visitorIdToTunnelChannelMap.remove(visitorId);
}
public static String getVisitorIdBySocketAddress(SocketAddress socketAddress) {
return socketAddressToVisitorIdMap.get(socketAddress);
}
public static void setVisitorIdToTunnelChannelMap(String visitorId, Channel tunnelChannel) {
visitorIdToTunnelChannelMap.put(visitorId, tunnelChannel);
}
public static Channel getTunnelChannelByVisitorId(String visitorId) {
return visitorIdToTunnelChannelMap.get(visitorId);
}
}