diff --git a/.gitignore b/.gitignore index 69b545a..d60b7b4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ HELP.md netty-log/* -target/ -!.mvn/wrapper/maven-wrapper.jar +target/** !**/src/main/** !**/src/test/** mvnw* diff --git a/README.md b/README.md index d4f6b14..4ac05c0 100644 --- a/README.md +++ b/README.md @@ -3,53 +3,78 @@ 基于netty的TCP/Http请求转发代理程序 ## 简介 - 在特定的网络环境或安全审计要求下,我们可能会面临网络被限定为单向访问的情况,本工具可以实现在单向网络中设置代理从而实现双向访问的目的。可简单用作内网穿透工具。 + 在特定的网络环境或安全审计要求下,我们可能会面临网络被限定为单向访问的情况,本工具可以实现在单向网络中设置代理从而实现双向访问的目的。可简单用作内网穿透工具。也可以配置成LVS负载均衡服务 ## 依赖/知识准备 - netty-高性能NIO通信框架 - tcp/ip通信基础知识 ## 工具原理说明 -1. [通信协议](代理程序通信协议.docx) +1. [通信协议](doc/代理程序通信协议.docx) ----- -2. 程序运行时序图![image](https://github.com/likedan130/netty-nat/blob/master/netty-nat%E6%97%B6%E5%BA%8F%E5%9B%BE.png) +2. 程序运行时序图![image](doc/netty-nat%E6%97%B6%E5%BA%8F%E5%9B%BE.png) ----- -3. 网络拓扑图![image](https://github.com/likedan130/netty-nat/blob/master/netty-nat%E7%BD%91%E8%B7%AF%E6%8B%93%E6%89%91%E5%9B%BE.png) - +3. 网络拓扑图![image](doc/netty-nat%E7%BD%91%E8%B7%AF%E6%8B%93%E6%89%91%E5%9B%BE.png) + ## 使用方式 1. **将项目克隆到本地** 2. **修改配置文件** 项目包含两个独立配置文件,分别为 - netty-nat - |--netty-client - |----properties.properties - |--netty-server - |----properties.properties +  netty-nat +  |--netty-client +  |----properties.yml +  |--netty-server +  |----properties.yml - client对应properties配置: - internal.server.port          代理程序服务端端口 - internal.server.host          代理程序服务端地址 - proxy.client.port         被代理程序的端口 - proxy.client.host         被代理程序的地址 - internal.channel.init.num      代理程序内部连接池初始大小 - internal.channel.max.idle.num   代理程序内部连接池最大空闲连接数 - internal.channel.min.idle.num    代理程序内部连接池最小空闲连接数 + + ```yaml + #内部连接池大小,内部连接只需转发外部与被代理服务间业务数据,可重用通道 + internal: + channel: + init: + num: 10 + #服务端ip和端口 + server: + host: 127.0.0.1 + port: 8083 + #隧道信息,一条完整的外部>>服务端>>客户端>>被代理服务间的通路称为隧道 + tunnel: + #tunnel示例,代理本地的mysql数据库服务和nacos服务 + #服务端监听端口 + - serverPort: 9000 + #客户端连接的被代理服务端口 + clientPort: 3306 + #客户端连接的被代理服务端口 + clientHost: 127.0.0.1 + - serverPort: 9001 + clientPort: 8848 + clientHost: 127.0.0.1 + #接入请求的接入密码 + password: '123456' + ``` + - server对应properties配置: - proxy.server.port         代理程序开发服务端口 - internal.server.port           代理程序内部连通端口(对应client配置中internal.server.port) + + ```yaml + #内部通信使用的端口,需要与客户端的internal.server.port值保持一致 + internal: + server: + port: 8083 + ``` 3. **打包** 项目中使用maven管理第三方依赖,打包使用maven-jar-plugin,自定义打包行为定义在项目根目录的assembly.xml中,打包时执行: -```mvn clean package -Dmaven.test.skip=ture``` + ```mvn clean package -Dmaven.test.skip=ture``` 4. **部署** 项目打包后获得 项目名称-版本号.zip -解压后获得 + 解压后获得 + - 项目主运行jar 项目名称-版本号.jar - 项目第三方包依赖目录 libs - - 项目配置文件目录 config -将解压后文件及目录保持当前层级关系上传至服务器 + - 项目配置文件目录 config + 将解压后文件及目录保持当前层级关系上传至服务器 5. **启动** 进入项目根目录 调整config目录下的log4j.properties为当前服务器相关配置 运行```java -Dlog4j.configuration=file:./config/log4j.properties -jar nat-server-1.0-SNAPSHOT.jar & ```启动服务端 观察到 InternalServer started on port xxxx......即表示服务启动成功 运行```java -Dlog4j.configuration=file:./config/log4j.properties -jar nat-client-1.0-SNAPSHOT.jar & ```启动客户端 - diff --git "a/doc/netty-nat\346\227\266\345\272\217\345\233\276.png" "b/doc/netty-nat\346\227\266\345\272\217\345\233\276.png" new file mode 100644 index 0000000..ecf9a86 Binary files /dev/null and "b/doc/netty-nat\346\227\266\345\272\217\345\233\276.png" differ diff --git "a/netty-nat\347\275\221\350\267\257\346\213\223\346\211\221\345\233\276.png" "b/doc/netty-nat\347\275\221\350\267\257\346\213\223\346\211\221\345\233\276.png" similarity index 100% rename from "netty-nat\347\275\221\350\267\257\346\213\223\346\211\221\345\233\276.png" rename to "doc/netty-nat\347\275\221\350\267\257\346\213\223\346\211\221\345\233\276.png" diff --git "a/doc/~$\347\250\213\345\272\217\351\200\232\344\277\241\345\215\217\350\256\256.docx" "b/doc/~$\347\250\213\345\272\217\351\200\232\344\277\241\345\215\217\350\256\256.docx" new file mode 100644 index 0000000..fcd42e3 Binary files /dev/null and "b/doc/~$\347\250\213\345\272\217\351\200\232\344\277\241\345\215\217\350\256\256.docx" differ diff --git "a/doc/\344\273\243\347\220\206\347\250\213\345\272\217\351\200\232\344\277\241\345\215\217\350\256\256.docx" "b/doc/\344\273\243\347\220\206\347\250\213\345\272\217\351\200\232\344\277\241\345\215\217\350\256\256.docx" new file mode 100644 index 0000000..8f25178 Binary files /dev/null and "b/doc/\344\273\243\347\220\206\347\250\213\345\272\217\351\200\232\344\277\241\345\215\217\350\256\256.docx" differ diff --git a/nat-client/src/main/java/client/InternalClient.java b/nat-client/src/main/java/client/InternalClient.java deleted file mode 100644 index 3040cad..0000000 --- a/nat-client/src/main/java/client/InternalClient.java +++ /dev/null @@ -1,133 +0,0 @@ -package client; - -import client.decoder.ByteToPojoDecoder; -import client.decoder.PojoToByteEncoder; -import client.group.ClientChannelGroup; -import client.handler.CustomEventHandler; -import client.handler.InternalClientHandler; -import core.cache.PropertiesCache; -import core.constant.FrameConstant; -import core.frame.loader.PropertiesLoader; -import io.netty.bootstrap.Bootstrap; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.codec.LengthFieldBasedFrameDecoder; -import io.netty.handler.timeout.IdleStateHandler; -import lombok.extern.slf4j.Slf4j; - -import java.util.concurrent.TimeUnit; - -/** - * @Author wneck130@gmail.com - * @Function netty客户端,用于和代理程序的服务端建立连接,以连接池的方式存在,传输代理的业务数据 - */ -@Slf4j -public class InternalClient extends BaseClient { - - /** - * 连接延时,防止过多消耗系统资源 - */ - public static int CONNECTION_DELAY = 1; - public static Bootstrap client = new Bootstrap(); - public static int initNum; - /** - * 客户端重连、扩展标志位,正在连接中时,对外部的连接建立命令静默 - */ - public static boolean connectingFlag = false; - /** - * 代理程序内部通信使用的地址 - */ - private static String HOST = "internal.server.host"; - /** - * 代理程序内部通信使用的端口 - */ - private static String PORT = "internal.server.port"; - /** - * 代理程序内部连接的初始化数量 - */ - private static String INIT_NUM = "internal.channel.init.num"; - /** - * 心跳间隔,默认为1分钟 - */ - private static long HEARTBEAT_INTERVAL = 1L; - - public static boolean isChanging() { - return connectingFlag; - } - - public static void main(String[] args) throws Exception { - InternalClient internalClient = new InternalClient(); - internalClient.init(); - internalClient.start(); - ClientChannelGroup.printGroupState(); - } - - public void init() throws Exception { - new PropertiesLoader().load(System.getProperty("user.dir")); -// new PropertiesLoader().load("E:\\songwei\\workspace\\netty-nat\\nat-client"); - group = new NioEventLoopGroup(); - //定义线程组,处理读写和链接事件 - client.group(group) - .channel(NioSocketChannel.class) - .handler(new ChannelInitializer() { - @Override - protected void initChannel(NioSocketChannel ch) { - ch.pipeline().addFirst(new LengthFieldBasedFrameDecoder(FrameConstant.FRAME_MAX_BYTES, - FrameConstant.FRAME_LEN_INDEX, FrameConstant.FRAME_LEN_LEN)) - .addLast(new ByteToPojoDecoder()) - .addLast(new PojoToByteEncoder()) - .addLast(new IdleStateHandler(0, 0, HEARTBEAT_INTERVAL, TimeUnit.MINUTES)) - .addLast(new CustomEventHandler()) - .addLast(new InternalClientHandler()); - } - }); - } - - /** - * 启动内部连接 - * - * @throws Exception - */ - public void start() { - cache = PropertiesCache.getInstance(); - initNum = cache.getInt(INIT_NUM); - connect(initNum); - } - - /** - * 启动指定数量的内部的连接 - * 建立连接失败时,按照简单的延迟重连机制进行重连 - * - * @throws Exception - */ - public void connect(Integer num) { - cache = PropertiesCache.getInstance(); - //连接服务器 - if (ClientChannelGroup.getIdleInternalGroupSize() < num) { - connectingFlag = true; - try { - client.connect(cache.get(HOST), - cache.getInt(PORT)).addListener((future -> { - if (future.isSuccess()) { - connect(num); - CONNECTION_DELAY = 1; - } else { - BaseClient.scheduledExecutor.schedule(() -> connect(num), CONNECTION_DELAY, TimeUnit.SECONDS); - CONNECTION_DELAY = CONNECTION_DELAY * 2; - log.error("启动internalChannel失败," + CONNECTION_DELAY + "秒后重试!!!"); - } - })); - } catch (Exception e) { - e.printStackTrace(); - log.error("connect to InternalSever error : ", e); - BaseClient.scheduledExecutor.schedule(() -> connect(num), CONNECTION_DELAY, TimeUnit.SECONDS); - CONNECTION_DELAY = CONNECTION_DELAY * 2; - log.error("启动internalChannel失败," + CONNECTION_DELAY + "秒后重试!!!"); - } - } else { - connectingFlag = false; - } - - } -} diff --git a/nat-client/src/main/java/client/ProxyClient.java b/nat-client/src/main/java/client/ProxyClient.java deleted file mode 100644 index 2a58e9d..0000000 --- a/nat-client/src/main/java/client/ProxyClient.java +++ /dev/null @@ -1,50 +0,0 @@ -package client; - -import client.handler.ProxyClientHandler; -import core.cache.PropertiesCache; -import io.netty.bootstrap.Bootstrap; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.channel.socket.nio.NioSocketChannel; - -/** - * @Author wneck130@gmail.com - * @Function netty代理客户端,用于连接被代理的服务 - */ -public class ProxyClient extends BaseClient { - - /** - * 被代理服务的地址 - */ - private static String HOST = "proxy.client.host"; - - /** - * 被代理服务的端口 - */ - private static String PORT = "proxy.client.port"; - - public void init() { - //加载配置文件 - cache = PropertiesCache.getInstance(); - } - - public ChannelFuture start() throws Exception { - //通过Bootstrap启动服务端 - Bootstrap client = new Bootstrap(); - //定义线程组,处理读写和链接事件 - group = new NioEventLoopGroup(); - client.group(group) - .channel(NioSocketChannel.class) - .handler(new ChannelInitializer() { - @Override - protected void initChannel(NioSocketChannel ch) { - //加入自定义的handler - ch.pipeline() - .addLast(new ProxyClientHandler()); - } - }); - return client.connect(cache.get(HOST), cache.getInt(PORT)); - } - -} diff --git a/nat-client/src/main/java/client/decoder/ByteToPojoDecoder.java b/nat-client/src/main/java/client/decoder/ByteToPojoDecoder.java deleted file mode 100644 index 84054ab..0000000 --- a/nat-client/src/main/java/client/decoder/ByteToPojoDecoder.java +++ /dev/null @@ -1,74 +0,0 @@ -package client.decoder; - -import core.constant.FrameConstant; -import core.entity.Frame; -import core.utils.BufUtil; -import core.utils.ByteUtil; -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.ByteToMessageDecoder; -import lombok.extern.slf4j.Slf4j; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * @Author wneck130@gmail.com - * @function 解码器,字节转Java对象 - */ -@Slf4j -public class ByteToPojoDecoder extends ByteToMessageDecoder { - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { - if (in.readableBytes() < FrameConstant.FRAME_MIN_LEN) { - log.error("无法解析的消息: " + ByteUtil.toHexString(BufUtil.getArray(in))); - ctx.close(); - return; - } - byte pv = in.readByte(); - long serial = in.readLong(); - byte cmd = in.readByte(); - int len = in.readInt(); - if (in.readableBytes() != len) { - log.error("无法解析的消息: " + ByteUtil.toHexString(BufUtil.getArray(in))); - ctx.close(); - return; - } - Frame frame = new Frame(); - frame.setPv(pv); - frame.setSerial(serial); - frame.setCmd(cmd); - frame.setLen(len); - try { - switch (cmd) { - case 0x01: - break; - case 0x02: - log.debug("InternalClient:" + ctx.channel().id() + "收到心跳指令"); - break; - case 0x03: - log.debug("InternalClient:" + ctx.channel().id() + "收到建立代理连接指令"); - break; - case 0x04: - break; - case (byte) 0xFF: - byte[] dataBytes = new byte[len]; - in.readBytes(dataBytes); - Map data = new HashMap<>(); - data.put("data", dataBytes); - frame.setData(data); - break; - default: - log.error("无法解析的消息: " + ByteUtil.toHexString(BufUtil.getArray(in))); - break; - } - } catch (Exception e) { - log.error("无法解析的消息: " + ByteUtil.toHexString(BufUtil.getArray(in))); - ctx.close(); - return; - } - out.add(frame); - } -} diff --git a/nat-client/src/main/java/client/decoder/PojoToByteEncoder.java b/nat-client/src/main/java/client/decoder/PojoToByteEncoder.java deleted file mode 100644 index 6caa08a..0000000 --- a/nat-client/src/main/java/client/decoder/PojoToByteEncoder.java +++ /dev/null @@ -1,36 +0,0 @@ -package client.decoder; - -import core.entity.Frame; -import core.enums.CommandEnum; -import core.utils.ByteUtil; -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToByteEncoder; -import lombok.extern.slf4j.Slf4j; - -import java.util.Arrays; - -/** - * @Author wneck130@gmail.com - * @function 编码器,Java对象转字节 - */ -@Slf4j -public class PojoToByteEncoder extends MessageToByteEncoder { - - @Override - protected void encode(ChannelHandlerContext ctx, Frame msg, ByteBuf out) throws Exception { - out.writeByte(msg.getPv()); - out.writeLong(msg.getSerial()); - out.writeByte(msg.getCmd()); - if (msg.getData() == null || msg.getData().get("data") == null) { - out.writeInt(0); - } else { - out.writeInt(((byte[]) msg.getData().get("data")).length); - out.writeBytes(((byte[]) msg.getData().get("data"))); - } - if (msg.getCmd() == CommandEnum.CMD_DATA_TRANSFER.getCmd()) { - byte[] print = Arrays.copyOf((byte[]) msg.getData().get("data"), 5); - log.debug("InternalClient" + ctx.channel().id() + "发送数据:" + ByteUtil.toHexString(print)); - } - } -} diff --git a/nat-client/src/main/java/client/group/ClientChannelGroup.java b/nat-client/src/main/java/client/group/ClientChannelGroup.java deleted file mode 100644 index 1746d6a..0000000 --- a/nat-client/src/main/java/client/group/ClientChannelGroup.java +++ /dev/null @@ -1,213 +0,0 @@ -package client.group; - -import client.BaseClient; -import client.InternalClient; -import client.ProxyClient; -import core.cache.PropertiesCache; -import io.netty.channel.Channel; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelId; -import io.netty.channel.group.ChannelGroup; -import io.netty.channel.group.DefaultChannelGroup; -import io.netty.util.concurrent.GlobalEventExecutor; -import lombok.extern.slf4j.Slf4j; - -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; - -/** - * @Author wneck130@gmail.com - * @function 客户端连接组,管理所有客户端的TCP连接 - */ -@Slf4j -public class ClientChannelGroup { - - private static PropertiesCache cache = PropertiesCache.getInstance(); - - private static Long CONNECT_PROXY_TIMEOUT = 3L; - - /** - * channel对,将服务端与客户端的channel进行配对,便于消息转发 - * key为连接池中内部连接的channelId,value为proxy服务的channelId - */ - private static Map channelPair = new ConcurrentHashMap<>(); - - /** - * 被代理服务的channel组 - */ - private static ChannelGroup proxyGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); - - /** - * 系统内部连接池的channel组 - */ - private static ChannelGroup internalGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); - - /** - * 内部服务的channel组 - */ - private static ChannelGroup idleInternalGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); - - public static ChannelGroup getIdleInternalGroup() { - return idleInternalGroup; - } - - public static int getIdleInternalGroupSize() { - return idleInternalGroup.size(); - } - - public static Map getChannelPair() { - return channelPair; - } - - public static ChannelGroup getInternalGroup() { - return internalGroup; - } - - public static void addProxyChannel(Channel channel) { - proxyGroup.add(channel); - } - - public static void removeProxyChannel(Channel channel) { - proxyGroup.remove(channel); - } - - public static void addInternalChannel(Channel channel) { - internalGroup.add(channel); - } - - public static void removeInternalChannel(Channel channel) throws Exception { - internalGroup.remove(channel); - } - - public static void removeIdleInternalChannel(Channel channel) throws Exception { - idleInternalGroup.remove(channel); - //检查空闲连接是否小于最小空闲连接数 - if (idleInternalGroup.size() < cache.getInt("internal.channel.min.idle.num")) { - InternalClient internalClient = new InternalClient(); - BaseClient.threadPoolExecutor.execute(() -> { - if (!InternalClient.isChanging()) { - internalClient.connect(5); - log.debug("InternalChannel当前连接数{}少于{},触发连接池扩容!!!", idleInternalGroup.size(), - cache.getInt("internal.channel.min.idle.num")); - } - }); - } - } - - public static void addIdleInternalChannel(Channel channel) { - idleInternalGroup.add(channel); - //检查空闲连接是否大于最大空闲连接数,成立则关闭当前连接 - if (idleInternalGroup.size() > cache.getInt("internal.channel.max.idle.num")) { - log.debug("InternalChannel当前连接数{}超过{},触发连接池缩减!!!", idleInternalGroup.size(), - cache.getInt("internal.channel.max.idle.num")); - channel.close(); - } - } - - public static void addChannelPair(Channel internalChannel, Channel proxyChannel) { - channelPair.put(internalChannel.id(), proxyChannel.id()); - } - - public static void removeChannelPair(ChannelId internalChannelId, ChannelId proxyChannelId) throws Exception { - channelPair.remove(internalChannelId, proxyChannelId); - } - - public static void removeChannelPair(ChannelId internalChannelId) throws Exception { - channelPair.remove(internalChannelId); - } - - /** - * 释放占用的连接池连接 - * - * @param channel - */ - public static void releaseInternalChannel(Channel channel) { - internalGroup.remove(channel); - addIdleInternalChannel(channel); - } - - /** - * 判断当前channel是否已经建立channelPair,没有建立channelPair的channel无法进行转发 - * - * @param channelId - * @return - */ - public static boolean channelPairExist(ChannelId channelId) { - if (channelPair.isEmpty()) { - return Boolean.FALSE; - } - if (channelPair.containsKey(channelId)) { - return Boolean.TRUE; - } - if (channelPair.containsValue(channelId)) { - return Boolean.TRUE; - } - return Boolean.FALSE; - } - - /** - * 根据内部channleId获取代理channel - * - * @param channelId - * @return - */ - public static Channel getProxyByInternal(ChannelId channelId) throws Exception { - ChannelId proxyChannelId = channelPair.get(channelId); - return proxyGroup.find(proxyChannelId); - } - - /** - * 根据代理channleId获取内部channel - * - * @param channelId - * @return - */ - public static Channel getInternalByProxy(ChannelId channelId) throws Exception { - List result = channelPair.entrySet().stream() - .filter(e -> Objects.equals(e.getValue(), channelId)) - .map((x) -> x.getKey()) - .collect(Collectors.toList()); - if (result.isEmpty() || result.size() != 1) { - log.error("找不到proxyChannel" + channelId + "!!!"); - return null; - } - return internalGroup.find(result.get(0)); - } - - public static void printGroupState() { - log.debug("当前channel情况:"); - log.debug("IdleInternalChannel数量:" + ClientChannelGroup.getIdleInternalGroup().size()); - log.debug("InternalChannel数量:" + ClientChannelGroup.getInternalGroup().size()); - log.debug("当前channelPair:"); - ClientChannelGroup.getChannelPair().entrySet().stream().forEach((entry) -> { - log.debug("[InternalChannel:" + entry.getKey() + ", ProxyChannel:" + entry.getValue() + "]"); - }); - } - - /** - * 统一创建与被代理服务间通信的方法,阻塞直到连接建立完成 - * - * @param ctx - */ - public static ChannelFuture connectToProxy(ChannelHandlerContext ctx) throws Exception { - //收到服务器的命令后主动建立与被代理服务之间的连接 - ProxyClient proxyClient = new ProxyClient(); - //启动代理服务 - proxyClient.init(); - ChannelFuture future = proxyClient.start(); - future.get(CONNECT_PROXY_TIMEOUT, TimeUnit.SECONDS); - Channel internalChannel = ctx.channel(); - if (future.isSuccess()) { - ClientChannelGroup.addChannelPair(internalChannel, future.channel()); - log.debug("建立连接: ClientInternal--[{}]--ClientProxy--[{}]--Responsor", internalChannel.id(), future.channel().id()); - ClientChannelGroup.removeIdleInternalChannel(internalChannel); - ClientChannelGroup.addInternalChannel(internalChannel); - } - return future; - } -} diff --git a/nat-client/src/main/java/client/handler/InternalClientHandler.java b/nat-client/src/main/java/client/handler/InternalClientHandler.java deleted file mode 100644 index 735c1cc..0000000 --- a/nat-client/src/main/java/client/handler/InternalClientHandler.java +++ /dev/null @@ -1,86 +0,0 @@ -package client.handler; - -import client.group.ClientChannelGroup; -import client.handler.processor.*; -import core.entity.Frame; -import core.enums.CommandEnum; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.handler.timeout.IdleState; -import io.netty.handler.timeout.IdleStateEvent; -import lombok.extern.slf4j.Slf4j; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * @Author wneck130@gmail.com - * @function 业务处理handler,所有协议命令在本类中处理 - */ -@Slf4j -public class InternalClientHandler extends SimpleChannelInboundHandler { - @Override - protected void channelRead0(ChannelHandlerContext ctx, Frame msg) throws Exception { - log.debug("Server数据:" + msg.toString()); - byte cmd = msg.getCmd(); - switch (cmd) { - //接入命令 - case 0x01: - new LoginProcessor().process(ctx, msg); - break; - //心跳命令 - case 0x02: - new HeartbeatProcessor().process(ctx, msg); - break; - //建立客户端代理连接 - case 0x03: - new ConnectToProxyProcessor().process(ctx, msg); - break; - //连接池回收 - case 0x04: - new ChannelRecycleProcessor().process(ctx, msg); - break; - //消息转发 - case (byte)0xff: - new DataTransferProcessor().process(ctx, msg); - break; - default: - throw new IllegalStateException("Unexpected value: " + cmd); - } - } - - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - ClientChannelGroup.addIdleInternalChannel(ctx.channel()); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - Channel internalChannel = ctx.channel(); - if (ClientChannelGroup.channelPairExist(internalChannel.id())) { - Channel proxyChannel = ClientChannelGroup.getProxyByInternal(internalChannel.id()); - if (proxyChannel == null) { - ClientChannelGroup.removeChannelPair(internalChannel.id()); - } else { - ClientChannelGroup.removeChannelPair(internalChannel.id(), proxyChannel.id()); - //internalChannel断开连接,则内部通信已经中断,防止TCP包有拆包,所以直接断开proxyChannel - ClientChannelGroup.removeProxyChannel(proxyChannel); - ClientChannelGroup.removeInternalChannel(internalChannel); - proxyChannel.flush().close(); - } - } - ClientChannelGroup.removeIdleInternalChannel(internalChannel); - } - - /** - * 通道异常触发 - * @param ctx - * @param cause - * @throws Exception - */ - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - log.error("ClientInternal[{}]发生异常:{}", ctx.channel().id(), cause.getStackTrace()); - ctx.close(); - } -} diff --git a/nat-client/src/main/java/client/handler/ProxyClientHandler.java b/nat-client/src/main/java/client/handler/ProxyClientHandler.java deleted file mode 100644 index 99f8c9d..0000000 --- a/nat-client/src/main/java/client/handler/ProxyClientHandler.java +++ /dev/null @@ -1,105 +0,0 @@ -package client.handler; - -import client.group.ClientChannelGroup; -import core.entity.Frame; -import core.enums.CommandEnum; -import core.utils.ByteUtil; -import io.netty.buffer.ByteBuf; -import io.netty.channel.Channel; -import io.netty.channel.ChannelFutureListener; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import lombok.extern.slf4j.Slf4j; - -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.util.HashMap; -import java.util.Map; - -/** - * @Author wneck130@gmail.com - * @function 代理客户端业务handler - */ -@Slf4j -public class ProxyClientHandler extends SimpleChannelInboundHandler { - @Override - protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { - byte[] print = new byte[5]; - msg.getBytes(0, print); - byte[] message = new byte[msg.readableBytes()]; - msg.readBytes(message); - //获取内部客户端 - if (ClientChannelGroup.channelPairExist(ctx.channel().id())) { - Channel internalChannel = ClientChannelGroup.getInternalByProxy(ctx.channel().id()); - log.debug("Responsor数据:" + ByteUtil.toHexString(message) - + "\n Server--[{}]--ClientInternal----ClientProxy<<[{}]< map = new HashMap<>(); - map.put("data", message); - Frame frame = new Frame(); - frame.setCmd(CommandEnum.CMD_DATA_TRANSFER.getCmd()); - frame.setData(map); - internalChannel.writeAndFlush(frame).addListener((ChannelFutureListener) future -> { - if (future.isSuccess()) { - log.debug("Responsor数据:" + ByteUtil.toHexString(message) - + "\n Server<<[{}]<>[{}]>>ClientInternal--[{}]--ClientProxy--[{}]--Responsor" - , internalChannel.id(), proxyChannel.id()); - ByteBuf response = Unpooled.buffer(); - response.writeBytes((byte[]) msg.getData().get("data")); - final ChannelId channelId = proxyChannel.id(); - proxyChannel.writeAndFlush(response).addListener((future -> { - if (future.isSuccess()) { - log.debug("Requestor数据:" + msg.toString() + - "\n Server--[{}]--ClientInternal--[{}]--ClientProxy>>[{}]>>Responsor" - , internalChannel.id(), channelId); - } - })); - } - log.debug("Requestor数据:" + msg.toString() + - "\n Server>>[{}]>>ClientInternal--[{}]--ClientProxy--[{}]--Responsor" - , internalChannel.id(), proxyChannel.id()); - ByteBuf response = Unpooled.buffer(); - response.writeBytes((byte[]) msg.getData().get("data")); - final ChannelId channelId = proxyChannel.id(); - proxyChannel.writeAndFlush(response).addListener((future -> { - if (future.isSuccess()) { - log.debug("Requestor数据:" + msg.toString() + - "\n Server--[{}]--ClientInternal--[{}]--ClientProxy>>[{}]>>Responsor" - , internalChannel.id(), channelId); - } - })); - } else { - //增加可靠性,proxy连接不存在时,重新建立配对并发送消息 - ProxyClient proxyClient = new ProxyClient(); - try { - //启动代理服务 - proxyClient.init(); - ChannelFuture future = proxyClient.start(); - future.get(); - if (future.isSuccess()) { - ClientChannelGroup.addChannelPair(internalChannel, future.channel()); - log.debug("建立连接: ClientInternal--[{}]--ClientProxy--[{}]--Responsor", internalChannel.id(), future.channel().id()); - ClientChannelGroup.removeIdleInternalChannel(internalChannel); - ClientChannelGroup.addInternalChannel(internalChannel); - Channel proxyChannel = future.channel(); - ByteBuf response = Unpooled.buffer(); - response.writeBytes((byte[]) msg.getData().get("data")); - proxyChannel.writeAndFlush(response).addListener((sendFuture -> { - if (sendFuture.isSuccess()) { - log.debug("Requestor数据:" + msg.toString() + - "\n Server--[{}]--ClientInternal--[{}]--ClientProxy>>[{}]>>Responsor" - , internalChannel.id(), proxyChannel.id()); - } - })); - } - } catch (Exception e) { - e.printStackTrace(); - ctx.close(); - } - } - } -} diff --git a/nat-client/src/main/java/client/handler/processor/HeartbeatProcessor.java b/nat-client/src/main/java/client/handler/processor/HeartbeatProcessor.java deleted file mode 100644 index 5352a7a..0000000 --- a/nat-client/src/main/java/client/handler/processor/HeartbeatProcessor.java +++ /dev/null @@ -1,17 +0,0 @@ -package client.handler.processor; - -import core.entity.Frame; -import core.processor.Processor; -import io.netty.channel.ChannelHandlerContext; - -/** - * @Author wneck130@gmail.com - * @function - */ -public class HeartbeatProcessor implements Processor { - - @Override - public void process(ChannelHandlerContext ctx, Frame msg) throws Exception { - //TODO 预留心跳命令,对于长连接的保持有特殊需要时启用心跳 - } -} diff --git a/nat-client/src/main/java/client/handler/processor/LoginProcessor.java b/nat-client/src/main/java/client/handler/processor/LoginProcessor.java deleted file mode 100644 index bb39e5f..0000000 --- a/nat-client/src/main/java/client/handler/processor/LoginProcessor.java +++ /dev/null @@ -1,17 +0,0 @@ -package client.handler.processor; - -import core.entity.Frame; -import core.processor.Processor; -import io.netty.channel.ChannelHandlerContext; - -/** - * @Author wneck130@gmail.com - * @Function 登录命令相应处理器 - */ -public class LoginProcessor implements Processor { - - @Override - public void process(ChannelHandlerContext ctx, Frame byteBuf) throws Exception { - //TODO 预留接入命令,在安全等级有需要的时候通过接入命令认证每一条连接 - } -} diff --git a/nat-client/src/main/java/client/internal/InternalNettyClient.java b/nat-client/src/main/java/client/internal/InternalNettyClient.java new file mode 100644 index 0000000..a90f92a --- /dev/null +++ b/nat-client/src/main/java/client/internal/InternalNettyClient.java @@ -0,0 +1,186 @@ +package client.internal; + +import client.internal.decoder.ByteToPojoDecoder; +import client.internal.decoder.PojoToByteEncoder; +import core.netty.group.ClientChannelGroup; +import client.internal.handler.CustomEventHandler; +import client.internal.handler.InternalClientHandler; +import client.internal.handler.processor.constant.ProcessorEnum; +import core.constant.FrameConstant; +import core.entity.Frame; +import core.entity.Tunnel; +import core.netty.group.channel.strategy.constant.ForkStrategyEnum; +import core.netty.handler.MessageSendFilter; +import core.netty.handler.MessageReceiveFilter; +import core.netty.stater.client.BaseClient; +import core.netty.stater.client.NettyClient; +import core.properties.cache.PropertiesCache; +import core.properties.loader.YamlLoader; +import core.utils.ByteUtil; +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.handler.codec.LengthFieldBasedFrameDecoder; +import io.netty.handler.timeout.IdleStateHandler; +import lombok.extern.slf4j.Slf4j; + +import java.nio.charset.StandardCharsets; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.concurrent.TimeUnit; + +/** + * @author wneck130@gmail.com + * @Description: + * @date 2021/9/29 + */ +@Slf4j +public class InternalNettyClient extends BaseClient implements NettyClient { + + /** + * 代理程序内部通信使用的地址 + */ + public static String HOST = "internal.server.host"; + /** + * 代理程序内部通信使用的端口 + */ + public static String PORT = "internal.server.port"; + /** + * 代理程序内部连接的初始化数量 + */ + public static String INIT_NUM = "internal.channel.init.num"; + /** + * 心跳间隔,默认为1分钟 + */ + private static long HEARTBEAT_INTERVAL = 1L; + + public Bootstrap client = new Bootstrap(); + + public static int initNum; + + /** + * 连接延时,防止过多消耗系统资源 + */ + public int CONNECTION_DELAY = 1; + + public static void main(String[] args) throws Exception { + InternalNettyClient internalClient = new InternalNettyClient(); + new YamlLoader().load(internalClient.getClass().getResource("/").getPath()); + internalClient.start(); + ClientChannelGroup.printGroupState(); + } + + @Override + public void init() { + cache = PropertiesCache.getInstance(); + this.host = cache.get(HOST); + this.port = cache.getInt(PORT); + group = new NioEventLoopGroup(); + //定义线程组,处理读写和链接事件 + client.group(group) + .channel(NioSocketChannel.class) + .handler(new ChannelInitializer() { + @Override + protected void initChannel(NioSocketChannel ch) { + ch.pipeline().addFirst(new LengthFieldBasedFrameDecoder(FrameConstant.FRAME_MAX_BYTES, + FrameConstant.FRAME_LEN_INDEX, FrameConstant.FRAME_LEN_LEN)) + .addLast(new IdleStateHandler(0, 0, HEARTBEAT_INTERVAL, TimeUnit.MINUTES)) + .addLast(new ByteToPojoDecoder()) + .addLast(new PojoToByteEncoder()) + .addLast(new MessageReceiveFilter()) + .addLast(new MessageSendFilter()) + .addLast(new CustomEventHandler()) + .addLast(new InternalClientHandler()); + } + }); + } + + @Override + public void start() { + init(); + initNum = cache.getInt(INIT_NUM); + connect(initNum, host, port); + } + + /** + * 启动指定数量的内部的连接 + * 建立连接失败时,按照简单的延迟重连机制进行重连 + * + * @throws Exception + */ + public void connect(Integer num, String host, int port) { + //连接服务器 + if (ClientChannelGroup.internalGroup.size() < num) { + try { + client.connect(host,port).sync().addListener((future -> { + if (future.isSuccess()) { + connect(num, host, port); + CONNECTION_DELAY = 1; + } else { + BaseClient.scheduledExecutor.schedule(() -> connect(num, host, port), CONNECTION_DELAY, TimeUnit.SECONDS); + CONNECTION_DELAY = CONNECTION_DELAY * 2; + log.error("启动internalClient失败," + CONNECTION_DELAY + "秒后重试!!!"); + } + })); + } catch (Exception e) { + e.printStackTrace(); + log.error("connect to InternalSever error : ", e); + BaseClient.scheduledExecutor.schedule(() -> connect(num, host, port), CONNECTION_DELAY, TimeUnit.SECONDS); + CONNECTION_DELAY = CONNECTION_DELAY * 2; + log.error("启动internalClient失败," + CONNECTION_DELAY + "秒后重试!!!"); + } + } else { + //连接池启动完毕后开始注册隧道信息 + // 多次注册不会影响服务端的端口监听,防止有新增加的隧道信息需要注册,每次连接都重新同步隧道信息 + register(); + } + + } + + /** + * 向服务端发送隧道注册信息 + */ + public void register() { + //指定数量的连接创建完毕,向服务端注册tunnel信息 + try { + //获取tunnel信息 + List tunnels = cache.getList("tunnel"); + for (int i = 0; i < tunnels.size(); i++) { + LinkedHashMap tunnelMap = (LinkedHashMap) tunnels.get(i); + Tunnel tunnel = new Tunnel(); + Integer serverPort = (Integer) tunnelMap.get("serverPort"); + tunnel.setServerPort(serverPort); + tunnel.setClientHost((String) tunnelMap.get("clientHost")); + tunnel.setClientPort((Integer) tunnelMap.get("clientPort")); + ClientChannelGroup.addTunnel(serverPort, tunnel); + + Channel channel = ClientChannelGroup.forkChannel(ForkStrategyEnum.RANDOM); + + Frame frame = new Frame(); + frame.setReq(channel.id().asShortText()); + frame.setRes(FrameConstant.DEFAULT_CHANNEL_ID); + frame.setCmd(ProcessorEnum.LOGIN.getCmd()); + LinkedHashMap data = new LinkedHashMap<>(); + String password = cache.get("password"); + data.put("passwordLen", ByteUtil.fromInt(password.length())[3]); + data.put("password", password.getBytes(StandardCharsets.UTF_8)); + data.put("serverPort", new byte[]{ByteUtil.fromInt(tunnel.getServerPort())[2], + ByteUtil.fromInt(tunnel.getServerPort())[3]}); + String[] clientHostSegment = tunnel.getClientHost().split("\\."); + data.put("clientHost", new byte[]{ByteUtil.fromInt(Integer.parseInt(clientHostSegment[0]))[3], + ByteUtil.fromInt(Integer.parseInt(clientHostSegment[1]))[3], + ByteUtil.fromInt(Integer.parseInt(clientHostSegment[2]))[3], + ByteUtil.fromInt(Integer.parseInt(clientHostSegment[3]))[3]}); + data.put("clientPort", new byte[]{ByteUtil.fromInt(tunnel.getClientPort())[2], + ByteUtil.fromInt(tunnel.getClientPort())[3]}); + frame.setData(data); + channel.writeAndFlush(frame); + } + } catch (Exception e) { + log.error("向服务端注册隧道失败!!!"); + } + } +} diff --git a/nat-client/src/main/java/client/internal/decoder/ByteToPojoDecoder.java b/nat-client/src/main/java/client/internal/decoder/ByteToPojoDecoder.java new file mode 100644 index 0000000..434f5fa --- /dev/null +++ b/nat-client/src/main/java/client/internal/decoder/ByteToPojoDecoder.java @@ -0,0 +1,35 @@ +package client.internal.decoder; + +import client.internal.handler.processor.constant.ProcessorEnum; +import core.constant.FrameConstant; +import core.entity.Frame; +import core.netty.handler.processor.ProcessorManager; +import core.utils.BufUtil; +import core.utils.ByteUtil; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ByteToMessageDecoder; +import lombok.extern.slf4j.Slf4j; + +import java.util.List; + +/** + * @Author wneck130@gmail.com + * @function 解码器,字节转Java对象 + */ +@Slf4j +public class ByteToPojoDecoder extends ByteToMessageDecoder { + + @Override + protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { + if (in.readableBytes() < FrameConstant.FRAME_MIN_LEN) { + log.error("invalidate msg : " + ByteUtil.toHexString(BufUtil.getArray(in))); + ctx.close(); + return; + } + log.debug("收到消息:{}", ByteUtil.toHexString(BufUtil.getArray(in))); + byte cmd = in.getByte(FrameConstant.FRAME_CMD_INDEX); + Frame frame = ProcessorManager.getInstance(ProcessorEnum.getClassByCmd(cmd)).assemble(in); + out.add(frame); + } +} diff --git a/nat-client/src/main/java/client/internal/decoder/PojoToByteEncoder.java b/nat-client/src/main/java/client/internal/decoder/PojoToByteEncoder.java new file mode 100644 index 0000000..ba04953 --- /dev/null +++ b/nat-client/src/main/java/client/internal/decoder/PojoToByteEncoder.java @@ -0,0 +1,52 @@ +package client.internal.decoder; + +import core.constant.FrameConstant; +import core.entity.Frame; +import core.utils.BufUtil; +import core.utils.ByteUtil; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToByteEncoder; +import lombok.extern.slf4j.Slf4j; + +import java.nio.charset.StandardCharsets; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * @author wneck130@gmail.com + * @function POJO对象编码器,将POJO对象转化成字节流 + */ +@Slf4j +public class PojoToByteEncoder extends MessageToByteEncoder { + + @Override + protected void encode(ChannelHandlerContext ctx, Frame msg, ByteBuf out) throws Exception { + out.writeByte(msg.getPv()); + out.writeLong(msg.getSerial()); + out.writeBytes(msg.getReq().getBytes(StandardCharsets.UTF_8)); + out.writeBytes(msg.getRes().getBytes(StandardCharsets.UTF_8)); + out.writeByte(msg.getCmd()); + if (msg.getData() == null) { + out.writeInt(0); + } else { + //填充默认值 + out.writeInt(0); + LinkedHashMap dataMap = msg.getData(); + int length = dataMap.entrySet().stream().map(Map.Entry::getValue).mapToInt(value -> { + if (value instanceof Byte) { + out.writeByte((byte) value); + return 1; + } else if (value instanceof byte[]) { + out.writeBytes((byte[]) value); + return ((byte[]) value).length; + } else { + //TODO 待实现多种数据类型的字节转化 + return 0; + } + }).sum(); + out.setBytes(FrameConstant.FRAME_LEN_INDEX, ByteUtil.fromInt(length)); + log.debug("发送消息:{}", ByteUtil.toHexString(BufUtil.getArray(out))); + } + } +} diff --git a/nat-client/src/main/java/client/handler/CustomEventHandler.java b/nat-client/src/main/java/client/internal/handler/CustomEventHandler.java similarity index 76% rename from nat-client/src/main/java/client/handler/CustomEventHandler.java rename to nat-client/src/main/java/client/internal/handler/CustomEventHandler.java index 1ecbf83..de38a85 100644 --- a/nat-client/src/main/java/client/handler/CustomEventHandler.java +++ b/nat-client/src/main/java/client/internal/handler/CustomEventHandler.java @@ -1,7 +1,8 @@ -package client.handler; +package client.internal.handler; +import client.internal.handler.processor.constant.ProcessorEnum; +import core.constant.FrameConstant; import core.entity.Frame; -import core.enums.CommandEnum; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.handler.timeout.IdleState; @@ -26,7 +27,9 @@ public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exc IdleStateEvent event = (IdleStateEvent) evt; if (event.state() == IdleState.ALL_IDLE) { Frame frame = new Frame(); - frame.setCmd(CommandEnum.CMD_HEARTBEAT.getCmd()); + frame.setCmd(ProcessorEnum.HEARTBEAT.getCmd()); + frame.setReq(ctx.channel().id().asShortText()); + frame.setRes(FrameConstant.DEFAULT_CHANNEL_ID); ctx.writeAndFlush(frame); } } else { diff --git a/nat-client/src/main/java/client/internal/handler/InternalClientHandler.java b/nat-client/src/main/java/client/internal/handler/InternalClientHandler.java new file mode 100644 index 0000000..3232847 --- /dev/null +++ b/nat-client/src/main/java/client/internal/handler/InternalClientHandler.java @@ -0,0 +1,64 @@ +package client.internal.handler; + +import core.netty.group.ClientChannelGroup; +import client.internal.handler.processor.constant.ProcessorEnum; +import client.internal.InternalNettyClient; +import core.entity.Frame; +import core.netty.group.channel.message.MessageContext; +import core.netty.handler.processor.ProcessorManager; +import core.properties.cache.PropertiesCache; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import lombok.extern.slf4j.Slf4j; + +import java.util.Arrays; + +/** + * @Author wneck130@gmail.com + * @function 业务处理handler,所有协议命令在本类中处理 + */ +@Slf4j +public class InternalClientHandler extends SimpleChannelInboundHandler { + @Override + protected void channelRead0(ChannelHandlerContext ctx, Frame msg) throws Exception { + byte cmd = msg.getCmd(); + ProcessorManager.getInstance(ProcessorEnum.getClassByCmd(cmd)).request(ctx, msg); + } + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + if (ClientChannelGroup.internalGroup.size() > PropertiesCache.getInstance().getInt(InternalNettyClient.INIT_NUM)) { + ctx.close(); + return; + } + MessageContext messageContext = MessageContext.getInstance(); + //将所有指令的处理器都指派给messageContext作为监听器 + Arrays.stream(ProcessorEnum.values()).forEach(processorEnum -> { + messageContext.addResponseListener(ProcessorManager.getInstance(processorEnum.getClazz())); + }); + ClientChannelGroup.addInternal(ctx.channel()); + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + ClientChannelGroup.removeInternal(ctx.channel()); + log.debug("[internalClient:{}]断开连接", ctx.channel().id().asShortText()); + InternalNettyClient internalClient = new InternalNettyClient(); + if (ClientChannelGroup.internalGroup.size() < PropertiesCache.getInstance().getInt(InternalNettyClient.INIT_NUM)) { + internalClient.start(); + } + + } + + /** + * 通道异常触发 + * @param ctx + * @param cause + * @throws Exception + */ + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + log.error("ClientInternal[{}]发生异常:{}", ctx.channel().id(), cause.getStackTrace()); + ctx.close(); + } +} diff --git a/nat-client/src/main/java/client/internal/handler/processor/ChannelRecycleProcessor.java b/nat-client/src/main/java/client/internal/handler/processor/ChannelRecycleProcessor.java new file mode 100644 index 0000000..5732e26 --- /dev/null +++ b/nat-client/src/main/java/client/internal/handler/processor/ChannelRecycleProcessor.java @@ -0,0 +1,34 @@ +package client.internal.handler.processor; + +/** + * @Author wneck130@gmail.com + * @function internal连接回收命令处理器,server端检测到连接断开后发送给客户端,确保无用tcp连接被回收 + */ +//@Slf4j +//public class ChannelRecycleProcessor implements Processor { +// +// @Override +// public Frame assemble(ByteBuf in) throws Exception { +// return null; +// } +// +// @Override +// public void request(ChannelHandlerContext ctx, Frame msg) throws Exception { +// Channel internalChannel = ctx.channel(); +// log.debug("InternalChannel:"+ internalChannel.id() + " 回收连接!!!"); +// if (ClientChannelGroup.channelPairExist(internalChannel.id())) { +// ClientChannelGroup.printGroupState(); +// Channel proxyChannel = ClientChannelGroup.getProxyByInternal(internalChannel.id()); +// ClientChannelGroup.removeProxyChannel(proxyChannel); +// if (internalChannel == null) { +// log.error("与internalChannel:"+internalChannel.id()+"配对的proxyChannel为null"); +// return; +// } +// ClientChannelGroup.removeChannelPair(internalChannel.id(), ctx.channel().id()); +// ClientChannelGroup.releaseInternalChannel(internalChannel); +// } else { +// log.error("internalChannel:"+ internalChannel.id() + " 未找到配对关系!!!"); +// ClientChannelGroup.printGroupState(); +// } +// } +//} diff --git a/nat-client/src/main/java/client/internal/handler/processor/ConnectionExpandProcessor.java b/nat-client/src/main/java/client/internal/handler/processor/ConnectionExpandProcessor.java new file mode 100644 index 0000000..b3d1d18 --- /dev/null +++ b/nat-client/src/main/java/client/internal/handler/processor/ConnectionExpandProcessor.java @@ -0,0 +1,18 @@ +package client.internal.handler.processor; + +/** + * @Author wneck130@gmail.com + * @function + */ +//public class ConnectionExpandProcessor implements Processor { +// +// @Override +// public Frame assemble(ByteBuf in) throws Exception { +// return null; +// } +// +// @Override +// public void request(ChannelHandlerContext ctx, Frame msg) throws Exception { +// //客户端对建立连接池命令的响应,无业务需要暂时不实现 +// } +//} diff --git a/nat-client/src/main/java/client/internal/handler/processor/ConnectionReduceProcessor.java b/nat-client/src/main/java/client/internal/handler/processor/ConnectionReduceProcessor.java new file mode 100644 index 0000000..9d48179 --- /dev/null +++ b/nat-client/src/main/java/client/internal/handler/processor/ConnectionReduceProcessor.java @@ -0,0 +1,14 @@ +package client.internal.handler.processor; + +//public class ConnectionReduceProcessor implements Processor { +// +// @Override +// public Frame assemble(ByteBuf in) throws Exception { +// return null; +// } +// +// @Override +// public void request(ChannelHandlerContext ctx, Frame msg) throws Exception { +// +// } +//} diff --git a/nat-client/src/main/java/client/internal/handler/processor/DownStreamProcessor.java b/nat-client/src/main/java/client/internal/handler/processor/DownStreamProcessor.java new file mode 100644 index 0000000..3944a8a --- /dev/null +++ b/nat-client/src/main/java/client/internal/handler/processor/DownStreamProcessor.java @@ -0,0 +1,64 @@ +package client.internal.handler.processor; + +import core.netty.group.ClientChannelGroup; +import client.internal.handler.processor.constant.ProcessorEnum; +import core.constant.FrameConstant; +import core.entity.Frame; +import core.netty.handler.processor.Processor; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; + +import java.util.LinkedHashMap; +import java.util.Objects; + +/** + * @author wneck130@gmail.com + * @Description: 数据下发处理器(cmd:0xEE),由proxyServer发起请求,proxyClient回复响应 + * @date 2021/10/8 + */ +public class DownStreamProcessor implements Processor { + + @Override + public Frame assemble(ByteBuf in) throws Exception { + Frame frame = new Frame().quickHead(in); + byte[] dataBytes = new byte[in.readableBytes()]; + in.readBytes(dataBytes); + LinkedHashMap dataMap = new LinkedHashMap<>(); + dataMap.put("data", dataBytes); + frame.setData(dataMap); + return frame; + } + + @Override + public void request(ChannelHandlerContext ctx, Frame msg) throws Exception { + //转发来自服务端的数据 + String clientChannelId = msg.getRes(); + Channel proxyClientChannel = ClientChannelGroup.findProxy(clientChannelId); + ByteBuf downStream = Unpooled.buffer(); + downStream.writeBytes((byte[]) msg.getData().get("data")); + proxyClientChannel.writeAndFlush(downStream).addListener(future -> { + Frame response = new Frame(); + response.setPv(FrameConstant.RES_PV); + response.setSerial(msg.getSerial()); + response.setReq(msg.getReq()); + response.setRes(msg.getRes()); + response.setCmd(msg.getCmd()); + response.setLen(1); + LinkedHashMap dataMap = new LinkedHashMap<>(); + if (future.isSuccess()) { + dataMap.put("result", FrameConstant.RESULT_SUCCESS); + } else { + dataMap.put("result", FrameConstant.RESULT_FAIL); + } + response.setData(dataMap); + ctx.writeAndFlush(response); + }); + } + + @Override + public boolean supply(byte cmd) { + return Objects.equals(ProcessorEnum.DOWN_STREAM.getCmd(), cmd); + } +} diff --git a/nat-client/src/main/java/client/internal/handler/processor/HeartbeatProcessor.java b/nat-client/src/main/java/client/internal/handler/processor/HeartbeatProcessor.java new file mode 100644 index 0000000..e948d44 --- /dev/null +++ b/nat-client/src/main/java/client/internal/handler/processor/HeartbeatProcessor.java @@ -0,0 +1,43 @@ +package client.internal.handler.processor; + +import client.internal.handler.processor.constant.ProcessorEnum; +import core.entity.Frame; +import core.netty.handler.processor.Processor; +import core.utils.BufUtil; +import core.utils.ByteUtil; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import lombok.extern.slf4j.Slf4j; + +import java.util.Objects; +import java.util.UnknownFormatConversionException; + +/** + * @Author wneck130@gmail.com + * @function 心跳命令处理器(cmd:0x02),命令由internalClient发起请求,internalServer回复响应 + */ +@Slf4j +public class HeartbeatProcessor implements Processor { + + @Override + public Frame assemble(ByteBuf in) throws Exception { + try { + //解析协议公共部分 + Frame frame = new Frame().quickHead(in); + return frame; + } catch (Exception e) { + log.error("无法解析的消息: " + ByteUtil.toHexString(BufUtil.getArray(in))); + throw new UnknownFormatConversionException("无法解析的消息!!!"); + } + } + + @Override + public void request(ChannelHandlerContext ctx, Frame msg) throws Exception { + //TODO 预留心跳命令,对于长连接的保持有特殊需要时启用心跳 + } + + @Override + public boolean supply(byte cmd) { + return Objects.equals(ProcessorEnum.HEARTBEAT.getCmd(), cmd); + } +} diff --git a/nat-client/src/main/java/client/internal/handler/processor/LoginProcessor.java b/nat-client/src/main/java/client/internal/handler/processor/LoginProcessor.java new file mode 100644 index 0000000..30d728f --- /dev/null +++ b/nat-client/src/main/java/client/internal/handler/processor/LoginProcessor.java @@ -0,0 +1,67 @@ +package client.internal.handler.processor; + +import client.internal.handler.processor.constant.ProcessorEnum; +import core.constant.FrameConstant; +import core.entity.Frame; +import core.netty.group.ServerChannelGroup; +import core.netty.group.channel.message.ResponseEvent; +import core.netty.handler.processor.Processor; +import core.utils.BufUtil; +import core.utils.ByteUtil; +import io.netty.buffer.ByteBuf; +import io.netty.channel.Channel; +import lombok.extern.slf4j.Slf4j; + +import java.util.LinkedHashMap; +import java.util.Objects; +import java.util.UnknownFormatConversionException; + +/** + * @Author wneck130@gmail.com + * @Function 接入命令处理器(cmd:0x01),命令由internalClient发起请求,internalServer回复响应 + */ +@Slf4j +public class LoginProcessor implements Processor { + + @Override + public Frame assemble(ByteBuf in) throws Exception { + try { + //解析协议公共部分 + Frame frame = new Frame().quickHead(in); + //解析Data部分 + byte result = in.readByte(); + LinkedHashMap dataMap = new LinkedHashMap<>(1); + dataMap.put("result", result); + frame.setData(dataMap); + return frame; + } catch (Exception e) { + log.error("无法解析的消息: " + ByteUtil.toHexString(BufUtil.getArray(in))); + throw new UnknownFormatConversionException("无法解析的消息!!!"); + } + } + + @Override + public void response(ResponseEvent responseEvent) { + byte result = (byte)responseEvent.getResponse().getData().get("result"); + if (result == FrameConstant.RESULT_FAIL) { + log.error("接入失败!!!"); + } + } + + @Override + public void timeout(ResponseEvent responseEvent) { + try { + //超时后默认进行一次重试 + Channel internalChannel = ServerChannelGroup.forkChannel(responseEvent.getChannelId()); + internalChannel.writeAndFlush(responseEvent.getRequest()); + } catch (Exception e) { + log.error("channel[{}]处理超时指令CMD:{},SERIAL:{}重试失败", responseEvent.getChannelId(), + responseEvent.getRequest().getCmd(), responseEvent.getRequest().getSerial()); + } + } + + @Override + public boolean supply(byte cmd) { + return Objects.equals(ProcessorEnum.LOGIN.getCmd(), cmd); + } +} diff --git a/nat-client/src/main/java/client/internal/handler/processor/PreConnectProcessor.java b/nat-client/src/main/java/client/internal/handler/processor/PreConnectProcessor.java new file mode 100644 index 0000000..57f6325 --- /dev/null +++ b/nat-client/src/main/java/client/internal/handler/processor/PreConnectProcessor.java @@ -0,0 +1,106 @@ +package client.internal.handler.processor; + +import core.netty.group.ClientChannelGroup; +import client.internal.handler.processor.constant.ProcessorEnum; +import client.proxy.ProxyNettyClient; +import core.constant.FrameConstant; +import core.entity.Frame; +import core.netty.handler.processor.Processor; +import core.utils.BufUtil; +import core.utils.ByteUtil; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelHandlerContext; +import lombok.extern.slf4j.Slf4j; + +import java.util.LinkedHashMap; +import java.util.Objects; +import java.util.UnknownFormatConversionException; + +/** + * @Author wneck130@gmail.com + * @function 预创建连接命令处理器(cmd:0x03),命令由proxyServer发起请求,proxyClient回复响应 + */ +@Slf4j +public class PreConnectProcessor implements Processor { + + @Override + public Frame assemble(ByteBuf in) throws Exception { + try { + //解析协议公共部分 + Frame frame = new Frame().quickHead(in); + int hostSegment1 = in.readByte() & 0xFF; + int hostSegment2 = in.readByte() & 0xFF; + int hostSegment3 = in.readByte() & 0xFF; + int hostSegment4 = in.readByte() & 0xFF; + String host = new StringBuilder().append(hostSegment1) + .append(".") + .append(hostSegment2) + .append(".") + .append(hostSegment3) + .append(".") + .append(hostSegment4).toString(); + int port = in.readUnsignedShort(); + LinkedHashMap dataMap = new LinkedHashMap<>(); + dataMap.put("host", host); + dataMap.put("port", port); + frame.setData(dataMap); + return frame; + } catch (Exception e) { + log.error("无法解析的消息: " + ByteUtil.toHexString(BufUtil.getArray(in))); + throw new UnknownFormatConversionException("无法解析的消息!!!"); + } + } + + @Override + public void request(ChannelHandlerContext ctx, Frame msg) throws Exception { + try { + //收到服务器的命令后主动建立与被代理服务之间的连接 + String host = (String) msg.getData().get("host"); + int port = (Integer) msg.getData().get("port"); + ProxyNettyClient proxyNettyClient = new ProxyNettyClient(); + proxyNettyClient.setHost(host); + proxyNettyClient.setPort(port); + proxyNettyClient.start(); + ChannelFuture future = proxyNettyClient.getFuture(); + //创建失败 + if (future == null) { + response(ctx, msg, FrameConstant.RESULT_FAIL, FrameConstant.DEFAULT_CHANNEL_ID); + return; + } + //创建成功后缓存配对关系,发送响应消息 + ClientChannelGroup.addChannelPair(msg.getReq(), future.channel().id().asShortText()); + log.debug("创建channelPair:[serverChannel:{}], [clientChannel:{}]", msg.getReq(), + future.channel().id().asShortText()); + response(ctx, msg, FrameConstant.RESULT_SUCCESS, future.channel().id().asShortText()); + } catch (Exception e) { + e.printStackTrace(); + log.error("预创建连接请求异常!!!"); + } + } + + /** + * 拼接响应内容 + * @param ctx + * @param msg + * @param result + */ + public void response(ChannelHandlerContext ctx, Frame msg, byte result, String proxyChannelId) { + Frame response = new Frame(); + response.setPv(FrameConstant.RES_PV); + response.setSerial(msg.getSerial()); + response.setReq(msg.getReq()); + response.setRes(proxyChannelId); + response.setLen(1); + response.setCmd(msg.getCmd()); + LinkedHashMap dataMap = new LinkedHashMap<>(); + dataMap.put("result", result); + response.setData(dataMap); + ctx.writeAndFlush(response); + } + + @Override + public boolean supply(byte cmd) { + return Objects.equals(ProcessorEnum.PRE_CONNECT, cmd); + } +} diff --git a/nat-client/src/main/java/client/internal/handler/processor/UpStreamProcessor.java b/nat-client/src/main/java/client/internal/handler/processor/UpStreamProcessor.java new file mode 100644 index 0000000..eeb8e3e --- /dev/null +++ b/nat-client/src/main/java/client/internal/handler/processor/UpStreamProcessor.java @@ -0,0 +1,73 @@ +package client.internal.handler.processor; + +import client.internal.handler.processor.constant.ProcessorEnum; +import core.constant.FrameConstant; +import core.entity.Frame; +import core.netty.group.ServerChannelGroup; +import core.netty.group.channel.message.ResponseEvent; +import core.netty.handler.processor.Processor; +import core.utils.BufUtil; +import core.utils.ByteUtil; +import io.netty.buffer.ByteBuf; +import io.netty.channel.Channel; +import lombok.extern.slf4j.Slf4j; + +import java.util.LinkedHashMap; +import java.util.Objects; +import java.util.UnknownFormatConversionException; + +/** + * @Author wneck130@gmail.com + * @function 数据上传处理器(cmd:0xFF),命令由proxyClient发起请求,internalClient处理响应 + */ +@Slf4j +public class UpStreamProcessor implements Processor { + + @Override + public Frame assemble(ByteBuf in) throws Exception { + try { + Frame frame = new Frame().quickHead(in); + //解析协议data部分 + byte result = in.readByte(); + LinkedHashMap dataMap = new LinkedHashMap<>(); + dataMap.put("result", result); + frame.setData(dataMap); + return frame; + } catch (Exception e) { + log.error("无法解析的消息: " + ByteUtil.toHexString(BufUtil.getArray(in))); + throw new UnknownFormatConversionException("无法解析的消息!!!"); + } + } + + @Override + public void response(ResponseEvent responseEvent) { + try { + LinkedHashMap dataMap = responseEvent.getResponse().getData(); + byte result = (byte) dataMap.get("result"); + if (result == FrameConstant.RESULT_FAIL) { + Channel internalChannel = ServerChannelGroup.forkChannel(responseEvent.getChannelId()); + internalChannel.writeAndFlush(responseEvent.getRequest()); + } + } catch (Exception e) { + log.error("channel[{}]处理响应CMD:{},SERIAL:{}重试失败", responseEvent.getChannelId(), + responseEvent.getRequest().getCmd(), responseEvent.getRequest().getSerial()); + } + } + + @Override + public void timeout(ResponseEvent responseEvent) { + try { + //超时后默认进行一次重试 + Channel internalChannel = ServerChannelGroup.forkChannel(responseEvent.getChannelId()); + internalChannel.writeAndFlush(responseEvent.getRequest()); + } catch (Exception e) { + log.error("channel[{}]处理超时指令CMD:{},SERIAL:{}重试失败", responseEvent.getChannelId(), + responseEvent.getRequest().getCmd(), responseEvent.getRequest().getSerial(), e); + } + } + + @Override + public boolean supply(byte cmd) { + return Objects.equals(ProcessorEnum.UP_STREAM.getCmd(), cmd); + } +} diff --git a/nat-client/src/main/java/client/internal/handler/processor/constant/ProcessorEnum.java b/nat-client/src/main/java/client/internal/handler/processor/constant/ProcessorEnum.java new file mode 100644 index 0000000..562212b --- /dev/null +++ b/nat-client/src/main/java/client/internal/handler/processor/constant/ProcessorEnum.java @@ -0,0 +1,66 @@ +package client.internal.handler.processor.constant; + +import client.internal.handler.processor.*; +import core.netty.handler.processor.Processor; + +import java.util.Objects; + +/** + * @author wneck130@gmail.com + * @Description: + * @date 2021/9/24 + */ +public enum ProcessorEnum { + /** + * 登录处理器 + */ + LOGIN((byte)0x01, LoginProcessor.class), + /** + * 心跳处理器 + */ + HEARTBEAT((byte)0x02, HeartbeatProcessor.class), + /** + * 客户端预连接被代理服务处理器 + */ + PRE_CONNECT((byte)0x03, PreConnectProcessor.class), + /** + * 数据传输处理器 + */ + DOWN_STREAM((byte)0xEE, DownStreamProcessor.class), + /** + * + */ + UP_STREAM((byte)0xFF, UpStreamProcessor.class) + ; + + private byte cmd; + + private Class clazz; + + ProcessorEnum(byte cmd, Class clazz) { + this.cmd = cmd; + this.clazz = clazz; + } + + public byte getCmd() { + return cmd; + } + + public Class getClazz() { + return clazz; + } + + /** + * 根据cmd获取对应class对象 + * @param cmd + * @return + */ + public static Class getClassByCmd(byte cmd) { + for (ProcessorEnum processorEnum : ProcessorEnum.values()) { + if (Objects.equals(processorEnum.getCmd(), cmd)) { + return processorEnum.getClazz(); + } + } + throw new EnumConstantNotPresentException(ProcessorEnum.class, cmd + "枚举常量不存在"); + } +} diff --git a/nat-client/src/main/java/client/proxy/ProxyNettyClient.java b/nat-client/src/main/java/client/proxy/ProxyNettyClient.java new file mode 100644 index 0000000..5231d20 --- /dev/null +++ b/nat-client/src/main/java/client/proxy/ProxyNettyClient.java @@ -0,0 +1,52 @@ +package client.proxy; + +import client.proxy.handler.ProxyClientHandler; +import client.proxy.handler.TcpInLogHandler; +import client.proxy.handler.TcpOutLogHandler; +import core.netty.stater.client.BaseClient; +import core.netty.stater.client.NettyClient; +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioSocketChannel; +import lombok.extern.slf4j.Slf4j; + +/** + * @author wneck130@gmail.com + * @Description: + * @date 2021/10/8 + */ +@Slf4j +public class ProxyNettyClient extends BaseClient implements NettyClient{ + + @Override + public void init() { + } + + @Override + public void start() { + try { + //通过Bootstrap启动服务端 + Bootstrap client = new Bootstrap(); + //定义线程组,处理读写和链接事件 + group = new NioEventLoopGroup(); + client.group(group) + .channel(NioSocketChannel.class) + .handler(new ChannelInitializer() { + @Override + protected void initChannel(NioSocketChannel ch) { + //加入自定义的handler + ch.pipeline() + //inbound流日志记录 + .addLast(new TcpInLogHandler()) + //outbound流日志记录 + .addLast(new TcpOutLogHandler()) + .addLast(new ProxyClientHandler()); + } + }); + future = client.connect(host, port).sync(); + } catch (Exception e) { + log.error("启动ProxyClient连接到失败"); + } + } +} diff --git a/nat-client/src/main/java/client/proxy/handler/ProxyClientHandler.java b/nat-client/src/main/java/client/proxy/handler/ProxyClientHandler.java new file mode 100644 index 0000000..82a5ed3 --- /dev/null +++ b/nat-client/src/main/java/client/proxy/handler/ProxyClientHandler.java @@ -0,0 +1,86 @@ +package client.proxy.handler; + +import core.netty.group.ClientChannelGroup; +import client.internal.handler.processor.constant.ProcessorEnum; +import core.entity.Frame; +import core.utils.BufUtil; +import io.netty.buffer.ByteBuf; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import lombok.extern.slf4j.Slf4j; + +import java.util.LinkedHashMap; + +/** + * @Author wneck130@gmail.com + * @function 代理客户端业务handler,在收到被代理服务的消息时将其转发给服务端 + */ +@Slf4j +public class ProxyClientHandler extends SimpleChannelInboundHandler { + @Override + protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { + String clientChannelId = ctx.channel().id().asShortText(); + String serverChannelId = ClientChannelGroup.getServerChannel(clientChannelId); + //没有对应的服务端channel,直接返回 + if (serverChannelId.isEmpty()) { + long start = System.currentTimeMillis(); + while (System.currentTimeMillis() - start < 5000) { + serverChannelId = ClientChannelGroup.getServerChannel(clientChannelId); + if (!serverChannelId.isEmpty()) { + break; + } + Thread.sleep(200); + } + if (serverChannelId.isEmpty()) { + ctx.close(); + return; + } + } + Frame frame = new Frame(); + frame.setReq(clientChannelId); + frame.setRes(serverChannelId); + frame.setCmd(ProcessorEnum.UP_STREAM.getCmd()); + frame.setLen(msg.readableBytes()); + LinkedHashMap dataMap = new LinkedHashMap<>(); + dataMap.put("data", BufUtil.getArray(msg)); + frame.setData(dataMap); + Channel internalChannel = ClientChannelGroup.forkChannel(serverChannelId); + internalChannel.writeAndFlush(frame); + } + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + ClientChannelGroup.addProxy(ctx.channel()); + ClientChannelGroup.printGroupState(); + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + //清除缓存信息 + String clientChannelId = ctx.channel().id().asShortText(); + String serverChannelId = ClientChannelGroup.getServerChannel(clientChannelId); + ClientChannelGroup.removeChannelPair(serverChannelId, clientChannelId); + ClientChannelGroup.removeProxy(ctx.channel()); + } + + /** + * 通道异常触发 + * + * @param ctx + * @param cause + * @throws Exception + */ + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + Channel channel = ctx.channel(); + if (!channel.isActive()) { + log.debug("被代理客户端 -- " + channel.remoteAddress() + " 断开了连接!"); + cause.printStackTrace(); + ctx.close(); + } else { + ctx.fireExceptionCaught(cause); + log.error("channel异常:", cause); + } + } +} diff --git a/nat-client/src/main/java/client/proxy/handler/TcpInLogHandler.java b/nat-client/src/main/java/client/proxy/handler/TcpInLogHandler.java new file mode 100644 index 0000000..db85bd5 --- /dev/null +++ b/nat-client/src/main/java/client/proxy/handler/TcpInLogHandler.java @@ -0,0 +1,58 @@ +package client.proxy.handler; + +import core.entity.Frame; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import lombok.extern.slf4j.Slf4j; + +import java.net.InetSocketAddress; + +/** + * @author wneck130@gmail.com + * @Description: + * @date 2021/9/27 + */ +@Slf4j +public class TcpInLogHandler extends SimpleChannelInboundHandler { + + private String serverIP; + + private String serverPort; + + @Override + protected void channelRead0(ChannelHandlerContext ctx, Frame msg) throws Exception { + log.debug("TcpProxyClient收到消息:{}", msg.toString()); + ctx.fireChannelRead(msg); + } + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + InetSocketAddress inSocket = (InetSocketAddress) ctx.channel().remoteAddress(); + serverIP = inSocket.getAddress().getHostAddress(); + serverPort = String.valueOf(inSocket.getPort()); + log.debug("成功创建与服务{}:{}的连接,Channel[{}]", serverIP, serverPort, ctx.channel().id()); + super.channelActive(ctx); + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + log.debug("关闭与服务{}:{}的连接,成功回收Channel[{}]", serverIP, serverPort, ctx.channel().id()); + super.channelInactive(ctx); + } + + /** + * 通道异常触发 + * @param ctx + * @param cause + * @throws Exception + */ + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + Channel channel = ctx.channel(); + if(!channel.isActive()){ + ctx.close(); + } + log.error("TcpProxyClient连接[{}]发生异常:{}", ctx.channel().id(), cause.getStackTrace()); + } +} diff --git a/nat-client/src/main/java/client/proxy/handler/TcpOutLogHandler.java b/nat-client/src/main/java/client/proxy/handler/TcpOutLogHandler.java new file mode 100644 index 0000000..fa6b8bd --- /dev/null +++ b/nat-client/src/main/java/client/proxy/handler/TcpOutLogHandler.java @@ -0,0 +1,24 @@ +package client.proxy.handler; + +import core.utils.BufUtil; +import core.utils.ByteUtil; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelOutboundHandlerAdapter; +import io.netty.channel.ChannelPromise; +import lombok.extern.slf4j.Slf4j; + +/** + * @author wneck130@gmail.com + * @Description: + * @date 2021/9/27 + */ +@Slf4j +public class TcpOutLogHandler extends ChannelOutboundHandlerAdapter { + + @Override + public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { + log.debug("ProxyClient发送消息:{}", ByteUtil.toHexString(BufUtil.getArray((ByteBuf) msg))); + super.write(ctx, msg, promise); + } +} diff --git a/nat-client/src/main/resources/properties.yml b/nat-client/src/main/resources/properties.yml new file mode 100644 index 0000000..818e3c5 --- /dev/null +++ b/nat-client/src/main/resources/properties.yml @@ -0,0 +1,23 @@ +#内部连接池大小,内部连接只需转发外部与被代理服务间业务数据,可重用通道 +internal: + channel: + init: + num: 10 +#服务端ip和端口 + server: + host: 127.0.0.1 + port: 8083 +#隧道信息,一条完整的外部>>服务端>>客户端>>被代理服务间的通路称为隧道 +tunnel: +#tunnel示例,代理本地的mysql数据库服务和nacos服务 + #服务端监听端口 + - serverPort: 9000 + #客户端连接的被代理服务端口 + clientPort: 3306 + #客户端连接的被代理服务端口 + clientHost: 127.0.0.1 + - serverPort: 9001 + clientPort: 8848 + clientHost: 127.0.0.1 +#接入请求的接入密码 +password: '123456' \ No newline at end of file diff --git a/nat-client/properties.properties b/nat-client/target/archive-tmp/fileSetFormatter.283016213.tmp/properties.properties similarity index 100% rename from nat-client/properties.properties rename to nat-client/target/archive-tmp/fileSetFormatter.283016213.tmp/properties.properties diff --git a/nat-client/target/archive-tmp/fileSetFormatter.283016213.tmp/properties.properties.958437838.filtered b/nat-client/target/archive-tmp/fileSetFormatter.283016213.tmp/properties.properties.958437838.filtered new file mode 100644 index 0000000..c8724a2 --- /dev/null +++ b/nat-client/target/archive-tmp/fileSetFormatter.283016213.tmp/properties.properties.958437838.filtered @@ -0,0 +1,7 @@ +internal.server.port=8080 +internal.server.host=47.92.215.60 +proxy.client.port=80 +proxy.client.host=127.0.0.1 +internal.channel.init.num=100 +internal.channel.max.idle.num=100 +internal.channel.min.idle.num=5 \ No newline at end of file diff --git a/nat-client/target/archive-tmp/fileSetFormatter.283016213.tmp/properties.yml b/nat-client/target/archive-tmp/fileSetFormatter.283016213.tmp/properties.yml new file mode 100644 index 0000000..f24c9d1 --- /dev/null +++ b/nat-client/target/archive-tmp/fileSetFormatter.283016213.tmp/properties.yml @@ -0,0 +1,15 @@ +internal: + channel: + init: + num: 1 + server: + host: 127.0.0.1 + port: 8082 +tunnel: + - serverPort: 9000 + clientPort: 3306 + clientHost: 127.0.0.1 + - serverPort: 9001 + clientPort: 8848 + clientHost: 127.0.0.1 +password: '123456' \ No newline at end of file diff --git a/nat-client/target/archive-tmp/fileSetFormatter.283016213.tmp/properties.yml.110789163.filtered b/nat-client/target/archive-tmp/fileSetFormatter.283016213.tmp/properties.yml.110789163.filtered new file mode 100644 index 0000000..f24c9d1 --- /dev/null +++ b/nat-client/target/archive-tmp/fileSetFormatter.283016213.tmp/properties.yml.110789163.filtered @@ -0,0 +1,15 @@ +internal: + channel: + init: + num: 1 + server: + host: 127.0.0.1 + port: 8082 +tunnel: + - serverPort: 9000 + clientPort: 3306 + clientHost: 127.0.0.1 + - serverPort: 9001 + clientPort: 8848 + clientHost: 127.0.0.1 +password: '123456' \ No newline at end of file diff --git a/nat-client/target/archive-tmp/fileSetFormatter.96054918.tmp/log4j.properties b/nat-client/target/archive-tmp/fileSetFormatter.96054918.tmp/log4j.properties new file mode 100644 index 0000000..5548f88 --- /dev/null +++ b/nat-client/target/archive-tmp/fileSetFormatter.96054918.tmp/log4j.properties @@ -0,0 +1,24 @@ +### ### +log4j.rootLogger = debug,stdout,D,E + +### Ϣ̧ ### +log4j.appender.stdout = org.apache.log4j.ConsoleAppender +log4j.appender.stdout.Target = System.out +log4j.appender.stdout.layout = org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n + +### DEBUG ϵ־=E://logs/error.log ### +log4j.appender.D = org.apache.log4j.DailyRollingFileAppender +log4j.appender.D.File = E://netty-log/clientLog.log +log4j.appender.D.Append = true +log4j.appender.D.Threshold = DEBUG +log4j.appender.D.layout = org.apache.log4j.PatternLayout +log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n + +### ERROR ϵ־=E://logs/error.log ### +log4j.appender.E = org.apache.log4j.DailyRollingFileAppender +log4j.appender.E.File =E://netty-log/error.log +log4j.appender.E.Append = true +log4j.appender.E.Threshold = ERROR +log4j.appender.E.layout = org.apache.log4j.PatternLayout +log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n \ No newline at end of file diff --git a/nat-client/target/archive-tmp/fileSetFormatter.96054918.tmp/log4j.properties.1510127011.filtered b/nat-client/target/archive-tmp/fileSetFormatter.96054918.tmp/log4j.properties.1510127011.filtered new file mode 100644 index 0000000..5548f88 --- /dev/null +++ b/nat-client/target/archive-tmp/fileSetFormatter.96054918.tmp/log4j.properties.1510127011.filtered @@ -0,0 +1,24 @@ +### ### +log4j.rootLogger = debug,stdout,D,E + +### Ϣ̧ ### +log4j.appender.stdout = org.apache.log4j.ConsoleAppender +log4j.appender.stdout.Target = System.out +log4j.appender.stdout.layout = org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n + +### DEBUG ϵ־=E://logs/error.log ### +log4j.appender.D = org.apache.log4j.DailyRollingFileAppender +log4j.appender.D.File = E://netty-log/clientLog.log +log4j.appender.D.Append = true +log4j.appender.D.Threshold = DEBUG +log4j.appender.D.layout = org.apache.log4j.PatternLayout +log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n + +### ERROR ϵ־=E://logs/error.log ### +log4j.appender.E = org.apache.log4j.DailyRollingFileAppender +log4j.appender.E.File =E://netty-log/error.log +log4j.appender.E.Append = true +log4j.appender.E.Threshold = ERROR +log4j.appender.E.layout = org.apache.log4j.PatternLayout +log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n \ No newline at end of file diff --git a/nat-client/target/classes/log4j.properties b/nat-client/target/classes/log4j.properties new file mode 100644 index 0000000..5548f88 --- /dev/null +++ b/nat-client/target/classes/log4j.properties @@ -0,0 +1,24 @@ +### ### +log4j.rootLogger = debug,stdout,D,E + +### Ϣ̧ ### +log4j.appender.stdout = org.apache.log4j.ConsoleAppender +log4j.appender.stdout.Target = System.out +log4j.appender.stdout.layout = org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n + +### DEBUG ϵ־=E://logs/error.log ### +log4j.appender.D = org.apache.log4j.DailyRollingFileAppender +log4j.appender.D.File = E://netty-log/clientLog.log +log4j.appender.D.Append = true +log4j.appender.D.Threshold = DEBUG +log4j.appender.D.layout = org.apache.log4j.PatternLayout +log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n + +### ERROR ϵ־=E://logs/error.log ### +log4j.appender.E = org.apache.log4j.DailyRollingFileAppender +log4j.appender.E.File =E://netty-log/error.log +log4j.appender.E.Append = true +log4j.appender.E.Threshold = ERROR +log4j.appender.E.layout = org.apache.log4j.PatternLayout +log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n \ No newline at end of file diff --git a/nat-client/target/classes/properties.yml b/nat-client/target/classes/properties.yml new file mode 100644 index 0000000..818e3c5 --- /dev/null +++ b/nat-client/target/classes/properties.yml @@ -0,0 +1,23 @@ +#内部连接池大小,内部连接只需转发外部与被代理服务间业务数据,可重用通道 +internal: + channel: + init: + num: 10 +#服务端ip和端口 + server: + host: 127.0.0.1 + port: 8083 +#隧道信息,一条完整的外部>>服务端>>客户端>>被代理服务间的通路称为隧道 +tunnel: +#tunnel示例,代理本地的mysql数据库服务和nacos服务 + #服务端监听端口 + - serverPort: 9000 + #客户端连接的被代理服务端口 + clientPort: 3306 + #客户端连接的被代理服务端口 + clientHost: 127.0.0.1 + - serverPort: 9001 + clientPort: 8848 + clientHost: 127.0.0.1 +#接入请求的接入密码 +password: '123456' \ No newline at end of file diff --git a/nat-client/target/libs/jackson-annotations-2.11.0.jar b/nat-client/target/libs/jackson-annotations-2.11.0.jar new file mode 100644 index 0000000..2c34567 Binary files /dev/null and b/nat-client/target/libs/jackson-annotations-2.11.0.jar differ diff --git a/nat-client/target/libs/jackson-core-2.11.0.jar b/nat-client/target/libs/jackson-core-2.11.0.jar new file mode 100644 index 0000000..6ed89ba Binary files /dev/null and b/nat-client/target/libs/jackson-core-2.11.0.jar differ diff --git a/nat-client/target/libs/jackson-databind-2.11.0.jar b/nat-client/target/libs/jackson-databind-2.11.0.jar new file mode 100644 index 0000000..458e1d3 Binary files /dev/null and b/nat-client/target/libs/jackson-databind-2.11.0.jar differ diff --git a/nat-client/target/libs/jackson-dataformat-yaml-2.12.5.jar b/nat-client/target/libs/jackson-dataformat-yaml-2.12.5.jar new file mode 100644 index 0000000..52b0896 Binary files /dev/null and b/nat-client/target/libs/jackson-dataformat-yaml-2.12.5.jar differ diff --git a/nat-client/target/libs/log4j-1.2.17.jar b/nat-client/target/libs/log4j-1.2.17.jar new file mode 100644 index 0000000..1d425cf Binary files /dev/null and b/nat-client/target/libs/log4j-1.2.17.jar differ diff --git a/nat-client/target/libs/log4j-api-2.13.2.jar b/nat-client/target/libs/log4j-api-2.13.2.jar new file mode 100644 index 0000000..afc89ff Binary files /dev/null and b/nat-client/target/libs/log4j-api-2.13.2.jar differ diff --git a/nat-client/target/libs/log4j-core-2.13.2.jar b/nat-client/target/libs/log4j-core-2.13.2.jar new file mode 100644 index 0000000..9b6bdff Binary files /dev/null and b/nat-client/target/libs/log4j-core-2.13.2.jar differ diff --git a/nat-client/target/libs/lombok-1.18.20.jar b/nat-client/target/libs/lombok-1.18.20.jar new file mode 100644 index 0000000..d3434f6 Binary files /dev/null and b/nat-client/target/libs/lombok-1.18.20.jar differ diff --git a/nat-client/target/libs/nat-core-1.0-SNAPSHOT.jar b/nat-client/target/libs/nat-core-1.0-SNAPSHOT.jar new file mode 100644 index 0000000..14aab9a Binary files /dev/null and b/nat-client/target/libs/nat-core-1.0-SNAPSHOT.jar differ diff --git a/nat-client/target/libs/netty-all-4.1.49.Final.jar b/nat-client/target/libs/netty-all-4.1.49.Final.jar new file mode 100644 index 0000000..6ecb025 Binary files /dev/null and b/nat-client/target/libs/netty-all-4.1.49.Final.jar differ diff --git a/nat-client/target/libs/slf4j-api-1.7.21.jar b/nat-client/target/libs/slf4j-api-1.7.21.jar new file mode 100644 index 0000000..2a5c33e Binary files /dev/null and b/nat-client/target/libs/slf4j-api-1.7.21.jar differ diff --git a/nat-client/target/libs/slf4j-log4j12-1.7.21.jar b/nat-client/target/libs/slf4j-log4j12-1.7.21.jar new file mode 100644 index 0000000..ff4fddd Binary files /dev/null and b/nat-client/target/libs/slf4j-log4j12-1.7.21.jar differ diff --git a/nat-client/target/libs/snakeyaml-1.27.jar b/nat-client/target/libs/snakeyaml-1.27.jar new file mode 100644 index 0000000..79bd430 Binary files /dev/null and b/nat-client/target/libs/snakeyaml-1.27.jar differ diff --git a/nat-client/target/maven-archiver/pom.properties b/nat-client/target/maven-archiver/pom.properties new file mode 100644 index 0000000..0e4bdaa --- /dev/null +++ b/nat-client/target/maven-archiver/pom.properties @@ -0,0 +1,4 @@ +#Created by Apache Maven 3.8.1 +version=1.0-SNAPSHOT +groupId=com.scrat +artifactId=nat-client diff --git a/nat-client/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/nat-client/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file mode 100644 index 0000000..631569c --- /dev/null +++ b/nat-client/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -0,0 +1,17 @@ +client\internal\decoder\PojoToByteEncoder.class +client\internal\handler\processor\UpStreamProcessor.class +client\internal\handler\processor\PreConnectProcessor.class +client\internal\handler\processor\constant\ProcessorEnum.class +client\internal\handler\CustomEventHandler.class +client\internal\InternalNettyClient.class +client\internal\handler\processor\LoginProcessor.class +client\internal\handler\processor\DownStreamProcessor.class +client\proxy\ProxyNettyClient$1.class +client\internal\decoder\ByteToPojoDecoder.class +client\internal\handler\processor\HeartbeatProcessor.class +client\proxy\handler\ProxyClientHandler.class +client\proxy\handler\TcpInLogHandler.class +client\proxy\handler\TcpOutLogHandler.class +client\internal\InternalNettyClient$1.class +client\internal\handler\InternalClientHandler.class +client\proxy\ProxyNettyClient.class diff --git a/nat-client/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/nat-client/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file mode 100644 index 0000000..e436f17 --- /dev/null +++ b/nat-client/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -0,0 +1,21 @@ +E:\workspace\netty-nat\nat-client\src\main\java\client\proxy\handler\ProxyClientHandler.java +E:\workspace\netty-nat\nat-client\src\main\java\client\proxy\handler\TcpOutLogHandler.java +E:\workspace\netty-nat\nat-client\src\main\java\client\internal\handler\InternalClientHandler.java +E:\workspace\netty-nat\nat-client\src\main\java\client\internal\handler\processor\LoginProcessor.java +E:\workspace\netty-nat\nat-client\src\main\java\client\internal\handler\processor\constant\ProcessorEnum.java +E:\workspace\netty-nat\nat-client\src\main\java\client\internal\handler\processor\ChannelRecycleProcessor.java +E:\workspace\netty-nat\nat-client\src\main\java\client\internal\handler\processor\ConnectionExpandProcessor.java +E:\workspace\netty-nat\nat-client\src\main\java\client\internal\handler\processor\PreConnectProcessor.java +E:\workspace\netty-nat\nat-client\src\main\java\client\ProxyClient.java +E:\workspace\netty-nat\nat-client\src\main\java\client\BaseClient.java +E:\workspace\netty-nat\nat-client\src\main\java\client\internal\handler\processor\DownStreamProcessor.java +E:\workspace\netty-nat\nat-client\src\main\java\client\proxy\handler\TcpInLogHandler.java +E:\workspace\netty-nat\nat-client\src\main\java\client\internal\decoder\PojoToByteEncoder.java +E:\workspace\netty-nat\nat-client\src\main\java\client\internal\handler\processor\ConnectionReduceProcessor.java +E:\workspace\netty-nat\nat-client\src\main\java\client\internal\handler\processor\UpStreamProcessor.java +E:\workspace\netty-nat\nat-client\src\main\java\client\proxy\ProxyNettyClient.java +E:\workspace\netty-nat\nat-client\src\main\java\client\InternalClient.java +E:\workspace\netty-nat\nat-client\src\main\java\client\internal\handler\processor\HeartbeatProcessor.java +E:\workspace\netty-nat\nat-client\src\main\java\client\internal\decoder\ByteToPojoDecoder.java +E:\workspace\netty-nat\nat-client\src\main\java\client\internal\InternalNettyClient.java +E:\workspace\netty-nat\nat-client\src\main\java\client\internal\handler\CustomEventHandler.java diff --git a/nat-client/target/nat-client-1.0-SNAPSHOT.jar b/nat-client/target/nat-client-1.0-SNAPSHOT.jar new file mode 100644 index 0000000..2cad9eb Binary files /dev/null and b/nat-client/target/nat-client-1.0-SNAPSHOT.jar differ diff --git a/nat-client/target/nat-client-1.0-SNAPSHOT.zip b/nat-client/target/nat-client-1.0-SNAPSHOT.zip new file mode 100644 index 0000000..62a4001 Binary files /dev/null and b/nat-client/target/nat-client-1.0-SNAPSHOT.zip differ diff --git a/nat-core/pom.xml b/nat-core/pom.xml index 1993392..4247627 100644 --- a/nat-core/pom.xml +++ b/nat-core/pom.xml @@ -25,10 +25,26 @@ org.projectlombok lombok + provided + + + org.slf4j + slf4j-api io.netty netty-all + + + + + + + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + 2.12.5 + \ No newline at end of file diff --git a/nat-core/src/main/java/core/annotation/CmdEventAnnotation.java b/nat-core/src/main/java/core/annotation/CmdEventAnnotation.java deleted file mode 100644 index 88fd65a..0000000 --- a/nat-core/src/main/java/core/annotation/CmdEventAnnotation.java +++ /dev/null @@ -1,10 +0,0 @@ -package core.annotation; - -/** - * Copyright (c) 2017 hadlinks, All Rights Reserved. - */ -public @interface CmdEventAnnotation { - - public String value() default ""; - -} diff --git a/nat-core/src/main/java/core/annotation/LogAnnotation.java b/nat-core/src/main/java/core/annotation/LogAnnotation.java deleted file mode 100644 index 7b69b3c..0000000 --- a/nat-core/src/main/java/core/annotation/LogAnnotation.java +++ /dev/null @@ -1,14 +0,0 @@ -package core.annotation; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Copyright (c) 2017 hadlinks, All Rights Reserved. - */ -@Retention(RetentionPolicy.RUNTIME) -public @interface LogAnnotation { - - public String value() default ""; - -} diff --git a/nat-core/src/main/java/core/cache/PropertiesCache.java b/nat-core/src/main/java/core/cache/PropertiesCache.java deleted file mode 100644 index fff3d62..0000000 --- a/nat-core/src/main/java/core/cache/PropertiesCache.java +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Copyright (c) 2017 hadlinks, All Rights Reserved. - */ -package core.cache; - -import java.util.concurrent.*; -import core.utils.ByteUtil; - -/** - * Project Name: huamei-farm - * Package Name: core.cache - * ClassName: PropertiesCache - * Function: 缓存项目的全局配置信息. - * date: 2017/3/15 10:53 - * @author songwei (songw@hadlinks.com) - * @since JDK 1.8 - */ -public class PropertiesCache { - - /** - * 私有的空参构造函数 - */ - private PropertiesCache(){} - - /** - * 单例 - * @return - */ - public static PropertiesCache getInstance(){ - return Holder.INSTANCE; - } - - /** - * 懒加载 - * @author songw - * - */ - private static class Holder{ - final static PropertiesCache INSTANCE = new PropertiesCache(); - } - - private final ConcurrentMap props = new ConcurrentHashMap(); - - - public ConcurrentMap getProps() { - return props; - } - - public String get(String key){ - return props.get(key); - } - - public Integer getInt(String key){ - return Integer.valueOf(props.get(key)); - } - - public Byte getByte(String key){ - return ByteUtil.parseHexString(props.get(key)); - } - - public byte[] getBytes(String key){ - return ByteUtil.parseHexStringToArray(props.get(key)); - } -} diff --git a/nat-core/src/main/java/core/constant/FrameConstant.java b/nat-core/src/main/java/core/constant/FrameConstant.java index fb968f5..89eff64 100644 --- a/nat-core/src/main/java/core/constant/FrameConstant.java +++ b/nat-core/src/main/java/core/constant/FrameConstant.java @@ -4,12 +4,17 @@ public class FrameConstant { /** * 协议头 */ - public static byte pv = (byte)0xAA; + public static byte REQ_PV = (byte)0x00; + public static byte RES_PV = (byte)0x01; - public static byte CMD_LOGIN = 0x01; + public static byte CMD_LOGIN = (byte)0x01; + + public static byte CMD_PRE_CONNECT = (byte)0x03; public static byte CMD_DATA_TRANSFER = (byte)0xFF; + public static String DEFAULT_CHANNEL_ID = "FFFFFFFF"; + /** * 通用成功响应 */ @@ -23,7 +28,7 @@ public class FrameConstant { /** * 数据帧命令字所在位置,起始位置为0 */ - public static int FRAME_CMD_INDEX = 9; + public static int FRAME_CMD_INDEX = 25; /** * 协议序号长度 @@ -33,7 +38,7 @@ public class FrameConstant { /** * 数据帧长度参数所在位置 */ - public static int FRAME_LEN_INDEX = 10; + public static int FRAME_LEN_INDEX = 26; /** * 数据帧长度参数的长度 @@ -100,11 +105,21 @@ public class FrameConstant { */ public static int TCP_SO_BACKLOG = 1024; + /** + * 内部通信channel阻止系统使用negale算法进行TCP并包发送 + */ + public static boolean TCP_NODELAY = true; + /** * TCP重连尝试次数 */ public static int TCP_CONNECTION_RETRY_NUM = 10; + /** + * 当端口处于TIME_WAIT时是否允许其他程序监听该端口,用于服务重启 + */ + public static boolean TCP_REUSE_ADDR = true; + /** * TCP重连尝试间隔扩大倍数 */ diff --git a/nat-core/src/main/java/core/entity/Frame.java b/nat-core/src/main/java/core/entity/Frame.java index 0bb8203..97c4d33 100644 --- a/nat-core/src/main/java/core/entity/Frame.java +++ b/nat-core/src/main/java/core/entity/Frame.java @@ -2,32 +2,75 @@ import core.constant.FrameConstant; import core.utils.ByteUtil; -import lombok.Data; +import io.netty.buffer.ByteBuf; +import lombok.Getter; +import lombok.Setter; -import java.util.HashMap; +import java.nio.charset.StandardCharsets; +import java.util.LinkedHashMap; import java.util.Map; -@Data +@Getter +@Setter public class Frame { - private byte pv = FrameConstant.pv; + private byte pv = FrameConstant.REQ_PV; private long serial = System.currentTimeMillis(); + private String req; + + private String res; + private byte cmd; private int len; - private Map data = new HashMap<>(); + private LinkedHashMap data = new LinkedHashMap<>(); @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("cmd:"+ ByteUtil.toHexString(cmd) + "\n"); sb.append("serial:"+ serial + "\n"); + sb.append("req:"+ req + "\n"); + sb.append("res:"+ res + "\n"); if (!data.isEmpty()) { - sb.append("data:" + ByteUtil.toHexString((byte[])data.get("data")) + "\n"); + data.forEach((key, value) -> { + if (value instanceof Byte) { + sb.append(key + ByteUtil.toHexString((byte)value) + "\n"); + } else if (value instanceof Byte[]) { + sb.append(key + ByteUtil.toHexString((byte[])value) + "\n"); + } else { + sb.append(key + value.toString() + "\n"); + } + }); } return sb.toString(); } + + /** + * 快速解析公共协议部分的内容 + * @param in + */ + public Frame quickHead(ByteBuf in) { + //解析协议公共部分 + byte pv = in.readByte(); + long serial = in.readLong(); + byte[] reqBytes = new byte[8]; + in.readBytes(reqBytes); + String req = new String(reqBytes, StandardCharsets.UTF_8); + byte[] resBytes = new byte[8]; + in.readBytes(resBytes); + String res = new String(resBytes, StandardCharsets.UTF_8); + byte cmd = in.readByte(); + int len = in.readInt(); + this.setPv(pv); + this.setSerial(serial); + this.setReq(req); + this.setRes(res); + this.setCmd(cmd); + this.setLen(len); + return this; + } } diff --git a/nat-core/src/main/java/core/entity/Tunnel.java b/nat-core/src/main/java/core/entity/Tunnel.java new file mode 100644 index 0000000..30a2f66 --- /dev/null +++ b/nat-core/src/main/java/core/entity/Tunnel.java @@ -0,0 +1,27 @@ +package core.entity; + +import lombok.Data; + +/** + * @author wneck130@gmail.com + * @Description: 通信隧道,从服务端到客户端的全链路形容,以serverPort作为唯一标识 + * @date 2021/9/24 + */ +@Data +public class Tunnel { + + /** + * 服务端端口号 + */ + private int serverPort; + + /** + * 客户端被代理服务IP + */ + private String clientHost; + + /** + * 客户端被代理服务端口号 + */ + private int clientPort; +} diff --git a/nat-core/src/main/java/core/enums/BuguEventEnum.java b/nat-core/src/main/java/core/enums/BuguEventEnum.java deleted file mode 100644 index b852d6e..0000000 --- a/nat-core/src/main/java/core/enums/BuguEventEnum.java +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright (c) 2017 hadlinks, All Rights Reserved. - */ -package core.enums; - -/** - * Project Name: bugu-farm-household - * Package Name: core.enums - * ClassName: BuguEventEnum - * Function: TODO ADD FUNCTION. - * date: 2017/10/30 14:45 - * @author songwei (songw@hadlinks.com) - * @since JDK 1.8 - */ -public enum BuguEventEnum { - - EVENT_TYPE_CMD_RESPONSE(1, "服务器主动下发命令收到响应事件"), - EVENT_TYPE_CMD_TIMEOUT(2, "服务器主动下发命令超时未收到响应事件"), - EVENT_TYPE_CMD_EXCEPTION(3, "服务器主动下发命令时发生异常"); - - BuguEventEnum(int type, String typeName) { - this.type = type; - this.typeName = typeName; - } - - private int type; - - private String typeName; - - public int getType() { - return type; - } - - public void setType(int type) { - this.type = type; - } - - public String getTypeName() { - return typeName; - } - - public void setTypeName(String typeName) { - this.typeName = typeName; - } -} diff --git a/nat-core/src/main/java/core/enums/CommandEnum.java b/nat-core/src/main/java/core/enums/CommandEnum.java deleted file mode 100644 index e61f9a8..0000000 --- a/nat-core/src/main/java/core/enums/CommandEnum.java +++ /dev/null @@ -1,110 +0,0 @@ -/** - * Copyright (c) 2017 hadlinks, All Rights Reserved. - */ -package core.enums; - -import core.utils.ByteUtil; - -/** - * Project Name: bugu-farm-household - * Package Name: core.enums - * ClassName: CommandEnum - * Function: TODO ADD FUNCTION. - * date: 2017/7/7 14:05 - * @author songwei (songw@hadlinks.com) - * @since JDK 1.8 - */ -public enum CommandEnum { - - //长连接设备相关指令(机械/电控设备) - CMD_LOGIN((byte)0x01, "接入命令"), - CMD_HEARTBEAT((byte)0x02, "心跳命令"), - CMD_START_PROXY_CLIENT((byte)0x03, "启动代理客户端"), - CMD_CHANNEL_RECYCLE((byte)0x04, "连接回收"), - CMD_DATA_TRANSFER((byte)0xFF, "业务数据转发"); - - - private byte cmd;//通信协议命令字 - - private String chName;//命令释义 - - CommandEnum(byte cmd, String chName) { - this.cmd = cmd; - this.chName = chName; - } - - public byte getCmd () { - return this.cmd; - } - - public String getHexCmd () { - return toHexString(this.cmd); - } - - public String getChName() { - return chName; - } - - /** - * 根据cmd的值获得一个CommandEnum实例 - * @param cmd - * @return CommandEnum - * @throws Exception - */ - public static CommandEnum getCommandEnumByCmd(String cmd) throws Exception { - byte cmdByte = ByteUtil.parseHexString(cmd); - for (CommandEnum constant : CommandEnum.values()) { - if (constant.getCmd() == cmdByte) { - return constant; - } - } - throw new Exception("Can't find the specific CommandEnum instance for input param : " + cmd); - } - - /** - * 根据cmd的值获得一个CommandEnum实例 - * @param cmd - * @return - * @throws Exception - */ - public static CommandEnum getCommandEnumByCmd(byte cmd) throws Exception { - for (CommandEnum constant : CommandEnum.values()) { - if (constant.getCmd() == cmd) { - return constant; - } - } - throw new Exception("Can't find the specific CommandEnum instance for input param : " + cmd); - } - - /** - * 根据枚举名称获取一个CommandEnum实例 - * @param name - * @return - * @throws Exception - */ - public static CommandEnum getCommandEnumByName (String name) throws Exception { - return Enum.valueOf(CommandEnum.class, name); - } - - /** - * 根据枚举名称或cmd获取一个CommandEnum实例 - * @param param - * @return - * @throws Exception - */ - public static CommandEnum getCommandEnum (String param) throws Exception { - try { - return getCommandEnumByName(param); - } catch (Exception e) { - return getCommandEnumByCmd(Integer.valueOf(param).byteValue()); - } - } - private String toHexString(byte b){ - String s = Integer.toHexString(b & 0xFF); - int len = s.length(); - if(len < 2){ - s = "0" + s; - } - return s.toUpperCase(); - } -} diff --git a/nat-core/src/main/java/core/enums/EventType.java b/nat-core/src/main/java/core/enums/EventType.java deleted file mode 100644 index 1fe14f7..0000000 --- a/nat-core/src/main/java/core/enums/EventType.java +++ /dev/null @@ -1,9 +0,0 @@ -package core.enums; - -/** - * Created by leesama on 2016/11/14. - */ -public enum EventType { - ONLINE, - OFFLINE; -} diff --git a/nat-core/src/main/java/core/enums/MessageEnum.java b/nat-core/src/main/java/core/enums/MessageEnum.java deleted file mode 100644 index fe3841f..0000000 --- a/nat-core/src/main/java/core/enums/MessageEnum.java +++ /dev/null @@ -1,39 +0,0 @@ -package core.enums; - -/** - * 用途:消息枚举 - * - * @author loumt(loumt@hadlinks.com) - */ -public enum MessageEnum { - - DEFAULT(0,"默认消息"), - //后台跑出去的消息 - NOTICE(1,"广告通知"), - //第三方消息 - PUSH(2,"推送子用户"), - //用户之间的消息 - FAULT(3,"故障消息"); - - private int code; - private String desc; - - MessageEnum(int code,String desc) { - this.code=code; - this.desc=desc; - } - - public String desc(){ - return this.desc; - } - public int code(){return this.code;} - - public String desc(int code){ - for (MessageEnum messageEnum:MessageEnum.values()){ - if(messageEnum.code==code){ - return this.desc; - } - } - return null; - } -} diff --git a/nat-core/src/main/java/core/enums/NumberEnum.java b/nat-core/src/main/java/core/enums/NumberEnum.java deleted file mode 100644 index f9b29e4..0000000 --- a/nat-core/src/main/java/core/enums/NumberEnum.java +++ /dev/null @@ -1,34 +0,0 @@ -package core.enums; - -/** - * 数字枚举 - */ -public enum NumberEnum { - - HEART_TATE_INTERVAL(15,"心跳间隔时间"); - - NumberEnum(int type, String typeName) { - this.type = type; - this.typeName = typeName; - } - - private int type; - - private String typeName; - - public int getType() { - return type; - } - - public void setType(int type) { - this.type = type; - } - - public String getTypeName() { - return typeName; - } - - public void setTypeName(String typeName) { - this.typeName = typeName; - } -} diff --git a/nat-core/src/main/java/core/enums/StringEnum.java b/nat-core/src/main/java/core/enums/StringEnum.java deleted file mode 100644 index cd80953..0000000 --- a/nat-core/src/main/java/core/enums/StringEnum.java +++ /dev/null @@ -1,17 +0,0 @@ -package core.enums; - -/** - * 字符枚举 - */ -public enum StringEnum { - LOGIN_PASSWORD("password"); - - StringEnum(String value) { - this.value = value; - } - private String value; - - public String getValue() { - return value; - } -} diff --git a/nat-core/src/main/java/core/netty/group/ClientChannelGroup.java b/nat-core/src/main/java/core/netty/group/ClientChannelGroup.java new file mode 100644 index 0000000..9fdd97d --- /dev/null +++ b/nat-core/src/main/java/core/netty/group/ClientChannelGroup.java @@ -0,0 +1,174 @@ +package core.netty.group; + +import core.entity.Tunnel; +import core.properties.cache.PropertiesCache; +import core.netty.group.channel.strategy.KeyBasedForkStrategy; +import core.netty.group.channel.strategy.StrategyManager; +import core.netty.group.channel.strategy.constant.ForkStrategyEnum; +import io.netty.channel.Channel; +import io.netty.channel.ChannelId; +import io.netty.channel.group.ChannelGroup; +import io.netty.channel.group.DefaultChannelGroup; +import io.netty.util.concurrent.GlobalEventExecutor; +import lombok.extern.slf4j.Slf4j; + +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @Author wneck130@gmail.com + * @function 客户端连接组,管理所有客户端的TCP连接 + */ +@Slf4j +public class ClientChannelGroup { + + /** + * 隧道信息,key为serverPort,value为tunnel完整信息 + */ + private static Map tunnels = new ConcurrentHashMap<>(); + + /** + * 添加tunnel + * @param serverPort + * @param tunnel + */ + public static void addTunnel(Integer serverPort, Tunnel tunnel) { + tunnels.put(serverPort, tunnel); + } + + /** + * 查找tunnel + * @param serverPort + * @return + */ + public static Tunnel getTunnel(Integer serverPort) { + return tunnels.get(serverPort); + } + + /** + * channel配对关系,key为server端channelId,value为client端channelId + * proxyServerChannel与proxyClientChannel配对 + * internalServerChannel与internalClientChannel配对 + */ + private static Map channelPair = new ConcurrentHashMap<>(); + + public static void addChannelPair(String serverChannelId, String clientChannelId) { + channelPair.put(serverChannelId, clientChannelId); + } + + public static void removeChannelPair(String serverChannelId, String clientChannelId) throws Exception{ + channelPair.remove(serverChannelId, clientChannelId); + } + + public static String getServerChannel(String clientChannelId) { + String serverChannelId = ""; + if (!channelPair.containsValue(clientChannelId)) { + return serverChannelId; + } + for(Map.Entry entry : channelPair.entrySet()) { + if (Objects.equals(entry.getValue(), clientChannelId)) { + serverChannelId = entry.getKey(); + } + } + return serverChannelId; + } + + /** + * 判断当前channel是否已经建立channelPair,没有建立channelPair的channel无法进行转发 + * + * @param channelId + * @return + */ + public static boolean channelPairExist(ChannelId channelId) { + if (channelPair.isEmpty()) { + return Boolean.FALSE; + } + if (channelPair.containsKey(channelId)) { + return Boolean.TRUE; + } + if (channelPair.containsValue(channelId)) { + return Boolean.TRUE; + } + return Boolean.FALSE; + } + + /** + * 从连接池中取出一条连接 + * @param forkStrategyEnum 获取连接的策略 + * @throws Exception + */ + public static synchronized Channel forkChannel(ForkStrategyEnum forkStrategyEnum) throws Exception{ + return StrategyManager.getInstance(forkStrategyEnum.getClazz()).fork(internalGroup); + } + + /** + * 从连接池中取出一条连接 + * @param basedKey 基于key值的channel获取策略,key值一般可以直接使用channelId,当使用此策略时,同一业务的数据具有顺序性 + * @throws Exception + */ + public static synchronized Channel forkChannel(String basedKey) throws Exception{ + KeyBasedForkStrategy keyBasedForkStrategy = (KeyBasedForkStrategy)StrategyManager + .getInstance(ForkStrategyEnum.KEY.getClazz()); + keyBasedForkStrategy.setKey(basedKey); + return keyBasedForkStrategy.fork(internalGroup); + } + + /** + * 被代理服务的channel组 + */ + private static Map proxyGroup = new ConcurrentHashMap<>(); + + /** + * 代理程序添加channel + * @param channel + */ + public static void addProxy(Channel channel) { + String channelId = channel.id().asShortText(); + proxyGroup.put(channelId, channel); + } + + /** + * 代理程序查找channel + * @param channelId + * @return + */ + public static Channel findProxy(String channelId) { + return proxyGroup.get(channelId); + } + + /** + * 代理程序删除channel + * @param channel + * @return + */ + public static boolean removeProxy(Channel channel) { + String channelId = channel.id().asShortText(); + return proxyGroup.remove(channelId, channel); + } + + /** + * 系统内部连接池的channel组 + */ + public static ChannelGroup internalGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); + + public static void addInternal(Channel channel) { + internalGroup.add(channel); + } + + public static boolean removeInternal(Channel channel) { + return internalGroup.remove(channel); + } + + + + public static void printGroupState() { + log.debug("当前channel情况:"); + log.debug("proxyChannel数量:" + ClientChannelGroup.proxyGroup.size()); + log.debug("InternalChannel数量:" + ClientChannelGroup.internalGroup.size()); + log.debug("当前channelPair:"); + ClientChannelGroup.channelPair.entrySet().stream().forEach((entry) -> { + log.debug("[InternalChannel:" + entry.getKey() + ", ProxyChannel:" + entry.getValue() + "]"); + }); + } +} diff --git a/nat-core/src/main/java/core/netty/group/ServerChannelGroup.java b/nat-core/src/main/java/core/netty/group/ServerChannelGroup.java new file mode 100644 index 0000000..7ffe0c6 --- /dev/null +++ b/nat-core/src/main/java/core/netty/group/ServerChannelGroup.java @@ -0,0 +1,226 @@ +package core.netty.group; + +import core.entity.Tunnel; +import core.netty.group.channel.strategy.KeyBasedForkStrategy; +import core.netty.group.channel.strategy.StrategyManager; +import core.netty.group.channel.strategy.constant.ForkStrategyEnum; +import io.netty.channel.Channel; +import io.netty.channel.ChannelId; +import io.netty.channel.group.ChannelGroup; +import io.netty.channel.group.DefaultChannelGroup; +import io.netty.util.concurrent.GlobalEventExecutor; +import lombok.extern.slf4j.Slf4j; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ScheduledFuture; + +/** + * @Author wneck130@gmail.com + * @Function 服务端channel组管理类 + */ +@Slf4j +public class ServerChannelGroup { + + /** + * 缓存的proxyServer对象,每一个proxyServer对应监听了目标端口的NioServerSocket服务 + * key为处理netty服务端连接的NioServerSocketChannel, value为对应的隧道信息 + */ + public static Map proxyServers = new HashMap<>(); + + /** + * proxyServers对象的add操作方法 + * @param nioServerSocketChannel + * @param tunnel + */ + public static void addProxyServers(Channel nioServerSocketChannel, Tunnel tunnel) { + proxyServers.put(nioServerSocketChannel, tunnel); + } + + /** + * 根据nioServerSocketChannel获取对应tunnelId,用于服务端向客户端发送预创建连接请求 + * @param nioServerSocketChannel + * @return + */ + public static Tunnel getTunnelByChannel(Channel nioServerSocketChannel) { + return proxyServers.get(nioServerSocketChannel); + } + + /** + * 检查proxyServer是否重复开启 + * @param tunnel + * @return + */ + public static boolean tunnelExists(Tunnel tunnel) { + for (Map.Entry entry : proxyServers.entrySet()) { + if (entry.getValue().getServerPort() == tunnel.getServerPort()) { + return true; + } + } + return false; + } + + /** + * proxyChannel每次发送数据都会期待在timeout时间内收到响应,如果超时则认为连接异常,断开proxyChannel重新服务 + * ScheduledFuture为当前channel对应eventloop中的一个定义关闭任务,超时后关闭连接 + */ + private static Map futureMap = new ConcurrentHashMap<>(); + + /** + * 被代理服务的channel组 + */ + private static Map proxyGroup = new ConcurrentHashMap<>(); + + /** + * 代理程序添加channel + * @param channel + */ + public static void addProxy(Channel channel) { + String channelId = channel.id().asShortText(); + proxyGroup.put(channelId, channel); + } + + /** + * 代理程序查找channel + * @param channelId + * @return + */ + public static Channel findProxy(String channelId) { + return proxyGroup.get(channelId); + } + + /** + * 代理程序删除channel + * @param channel + * @return + */ + public static boolean removeProxy(Channel channel) { + String channelId = channel.id().asShortText(); + return proxyGroup.remove(channelId, channel); + } + + + /** + * 内部服务的channel组 + */ + private static ChannelGroup internalGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); + + public static void addInternal(Channel channel) { + internalGroup.add(channel); + } + + public static boolean removeInternal(Channel channel) { + return internalGroup.remove(channel); + } + + + /** + * channel配对关系,key为server端channelId,value为client端channelId + * proxyServerChannel与proxyClientChannel配对 + * internalServerChannel与internalClientChannel配对 + */ + private static Map channelPair = new ConcurrentHashMap<>(); + + + /** + * 从连接池中取出一条连接 + * @param forkStrategyEnum 获取连接的策略 + * @throws Exception + */ + public static synchronized Channel forkChannel(ForkStrategyEnum forkStrategyEnum) throws Exception{ + return StrategyManager.getInstance(forkStrategyEnum.getClazz()).fork(internalGroup); + } + + /** + * 从连接池中取出一条连接 + * @param basedKey 基于key值的channel获取策略,key值一般可以直接使用channelId,当使用此策略时,同一业务的数据具有顺序性 + * @throws Exception + */ + public static synchronized Channel forkChannel(String basedKey) throws Exception{ + KeyBasedForkStrategy keyBasedForkStrategy = (KeyBasedForkStrategy)StrategyManager + .getInstance(ForkStrategyEnum.KEY.getClazz()); + keyBasedForkStrategy.setKey(basedKey); + return keyBasedForkStrategy.fork(internalGroup); + } + + public static void addChannelPair(String serverChannelId, String clientChannelId) { + channelPair.put(serverChannelId, clientChannelId); + } + + /** + * 根据serverChannelId获取配对的clientChannelId + * @param serverChannelId + * @return + */ + public static String getClientByServer(String serverChannelId) { + String clientChannelId = ""; + if (channelPair.containsKey(serverChannelId)) { + clientChannelId = channelPair.get(serverChannelId); + } + return clientChannelId; + } + + public static void removeChannelPair(Channel serverChannel, Channel clientChannel) throws Exception{ + channelPair.remove(serverChannel, clientChannel); + } + + /** + * 判断当前channel是否已经建立channelPair,没有建立channelPair的channel无法进行转发 + * @param channel + * @return + */ + public static boolean channelPairExist(Channel channel) { + if (channelPair.isEmpty()) { + return Boolean.FALSE; + } + if (channelPair.containsKey(channel)) { + return Boolean.TRUE; + } + if (channelPair.containsValue(channel)) { + return Boolean.TRUE; + } + return Boolean.FALSE; + } + + + + /** + * 打印当前channelGroup情况 + */ + public static void printGroupState() { + log.info("当前channel情况:\r\n" + + "ProxyChannel数量:" + ServerChannelGroup.proxyGroup.size() + "\r\n" + + "InternalChannel数量:" + ServerChannelGroup.internalGroup.size() + "\r\n" + + "当前channelPair:\r\n"); + ServerChannelGroup.channelPair + .forEach((key, value) -> log.debug("[ServerChannel:" + key + ", ClientChannel:" + value + "]\r\n")); + } + + /** + * 存储超时关闭的定时任务,如果已经存在任务,则cancel新任务,保持原任务不变 + * @param channelId + * @param future + */ + public static void addFuture(ChannelId channelId, ScheduledFuture future) { + try { + ScheduledFuture successOne = futureMap.putIfAbsent(channelId, future); + if (!Objects.equals(successOne, future)) { + future.cancel(true); + } + } catch (Exception e) { + e.printStackTrace(); + log.error("超时关闭proxyChannel异常!!!", e); + } + } + + public static void cancelFuture(ChannelId channelId) { + try { + futureMap.get(channelId).cancel(true); + } catch (Exception e) { + e.printStackTrace(); + log.error("关闭ScheduledFuture任务异常!!!", e); + } + } +} diff --git a/nat-core/src/main/java/core/netty/group/channel/message/MessageContext.java b/nat-core/src/main/java/core/netty/group/channel/message/MessageContext.java new file mode 100644 index 0000000..8115c54 --- /dev/null +++ b/nat-core/src/main/java/core/netty/group/channel/message/MessageContext.java @@ -0,0 +1,124 @@ +package core.netty.group.channel.message; + +import core.entity.Frame; +import core.netty.group.channel.message.receiver.listener.ResponseListener; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * @author wneck130@gmail.com + * @Description: + * @date 2021/9/28 + */ +public class MessageContext { + + public static ScheduledThreadPoolExecutor scheduled = new ScheduledThreadPoolExecutor(1); + + private MessageContext(){ + //对缓存的所有消息进行超时判断,间隔1秒 + scheduled.scheduleAtFixedRate(() -> + historyMessage.entrySet().stream().forEach(stringListEntry -> { + String channelId = stringListEntry.getKey(); + stringListEntry.getValue().stream() + .filter(request -> request.getSerial() < System.currentTimeMillis() - 30000L) + .forEach(timeoutRequest -> { + ResponseEvent responseEvent = new ResponseEvent(this); + responseEvent.setRequest(timeoutRequest); + responseEvent.setChannelId(channelId); + this.notifyTimeout(responseEvent); + }); + }), 1, 1, TimeUnit.SECONDS); + } + + public static MessageContext getInstance() { + return Holder.instance; + } + + private static class Holder { + private static MessageContext instance = new MessageContext(); + + } + + private List listeners = new ArrayList<>(); + + /** + * 发送历史消息合集,当发送消息后添加了响应监听器,则缓存当前发送的消息 + * Key为proxyServer的channelId,value为消息frame的serial字段值 + */ + private static Map> historyMessage = new ConcurrentHashMap<>(); + + public static void addHistoryFrame(String channelId, Frame frame) { + if (historyMessage.containsKey(channelId)) { + List frames = historyMessage.get(channelId); + if (frames == null) { + frames = new ArrayList<>(); + } + frames.add(frame); + historyMessage.put(channelId, frames); + } else { + List frames = new ArrayList<>(); + frames.add(frame); + historyMessage.put(channelId, frames); + } + } + + /** + * 清除消息发送记录 + * @param channelId + * @param frame + * @return + */ + private void removeHistory(String channelId, Frame frame) { + if (!historyMessage.containsKey(channelId)) { + return; + } + List frames = historyMessage.get(channelId); + if (frames == null || frames.isEmpty()) { + return; + } + frames.remove(frame); + historyMessage.put(channelId, frames); + } + + public static Frame getHistoryFrame(String channelId, Long serial) { + List historyRequest = historyMessage.get(channelId); + if (historyRequest == null) { + return null; + } + return historyRequest.stream().filter(frame -> Objects.equals(frame.getSerial(), serial)).findFirst().get(); + } + + public void addResponseListener(ResponseListener responseListener) { + if (!listeners.contains(responseListener)) { + listeners.add(responseListener); + } + } + + /** + * 响应超时消息 + */ + public void notifyTimeout(ResponseEvent responseEvent) { + listeners.forEach(responseListener -> responseListener.timeout(responseEvent)); + removeHistory(responseEvent.getChannelId(), responseEvent.getRequest()); + } + + /** + * 响应事件通知 + * @param responseEvent + */ + public void notifyResponse(ResponseEvent responseEvent) { + byte cmd = responseEvent.getRequest().getCmd(); + listeners.stream().filter(responseListener -> responseListener.supply(cmd)) + .forEach(responseListener -> responseListener.response(responseEvent)); + removeHistory(responseEvent.getChannelId(), responseEvent.getRequest()); + } + + + +} diff --git a/nat-core/src/main/java/core/netty/group/channel/message/ResponseEvent.java b/nat-core/src/main/java/core/netty/group/channel/message/ResponseEvent.java new file mode 100644 index 0000000..0b4ba7d --- /dev/null +++ b/nat-core/src/main/java/core/netty/group/channel/message/ResponseEvent.java @@ -0,0 +1,35 @@ +package core.netty.group.channel.message; + +import core.entity.Frame; +import lombok.Getter; +import lombok.Setter; + +import java.util.EventObject; + +/** + * @author wneck130@gmail.com + * @Description: + * @date 2021/9/28 + */ +@Setter +@Getter +public class ResponseEvent extends EventObject { + + private Frame request; + + private Frame response; + + private String channelId; + + /** + * Constructs a prototypical Event. + * + * @param source The object on which the Event initially occurred. + * @throws IllegalArgumentException if source is null. + */ + public ResponseEvent(Object source) { + super(source); + } + + +} diff --git a/nat-core/src/main/java/core/netty/group/channel/message/receiver/MessageReceiver.java b/nat-core/src/main/java/core/netty/group/channel/message/receiver/MessageReceiver.java new file mode 100644 index 0000000..028078d --- /dev/null +++ b/nat-core/src/main/java/core/netty/group/channel/message/receiver/MessageReceiver.java @@ -0,0 +1,20 @@ +package core.netty.group.channel.message.receiver; + +import core.entity.Frame; +import io.netty.buffer.ByteBuf; + +/** + * @author wneck130@gmail.com + * @Description: 消息接收器 + * @date 2021/10/11 + */ +public interface MessageReceiver { + + /** + * 接受消息时针对自定义协议解析数据帧,将协议中内容转化成Frame对象为下游提供数据输入 + * @param in + * @return + * @throws Exception + */ + Frame assemble(ByteBuf in) throws Exception; +} diff --git a/nat-core/src/main/java/core/netty/group/channel/message/receiver/listener/MessageListener.java b/nat-core/src/main/java/core/netty/group/channel/message/receiver/listener/MessageListener.java new file mode 100644 index 0000000..f826674 --- /dev/null +++ b/nat-core/src/main/java/core/netty/group/channel/message/receiver/listener/MessageListener.java @@ -0,0 +1,18 @@ +package core.netty.group.channel.message.receiver.listener; + +import core.netty.group.channel.message.receiver.MessageReceiver; + +/** + * @author wneck130@gmail.com + * @Description: 消息监听器 + * @date 2021/10/11 + */ +public interface MessageListener extends MessageReceiver { + + /** + * 根据数据帧的内容进行消息分类适配 + * @param cmd + * @return + */ + boolean supply(byte cmd); +} diff --git a/nat-core/src/main/java/core/netty/group/channel/message/receiver/listener/RequestListener.java b/nat-core/src/main/java/core/netty/group/channel/message/receiver/listener/RequestListener.java new file mode 100644 index 0000000..08a5983 --- /dev/null +++ b/nat-core/src/main/java/core/netty/group/channel/message/receiver/listener/RequestListener.java @@ -0,0 +1,22 @@ +package core.netty.group.channel.message.receiver.listener; + +import core.entity.Frame; +import io.netty.channel.ChannelHandlerContext; + +/** + * @author wneck130@gmail.com + * @Description: 请求类消息监听器 + * @date 2021/10/11 + */ +public interface RequestListener extends MessageListener { + + /** + * 消息请求处理方法 + * + * @param ctx netty channel上下文 + * @param msg 解析成Frame结构的TCP请求数据帧 + * @throws Exception + */ + default void request(ChannelHandlerContext ctx, Frame msg) throws Exception { + } +} diff --git a/nat-core/src/main/java/core/netty/group/channel/message/receiver/listener/ResponseListener.java b/nat-core/src/main/java/core/netty/group/channel/message/receiver/listener/ResponseListener.java new file mode 100644 index 0000000..afd8c77 --- /dev/null +++ b/nat-core/src/main/java/core/netty/group/channel/message/receiver/listener/ResponseListener.java @@ -0,0 +1,23 @@ +package core.netty.group.channel.message.receiver.listener; + +import core.netty.group.channel.message.ResponseEvent; + +/** + * @author wneck130@gmail.com + * @Description: 响应类消息监听器 + * @date 2021/10/11 + */ +public interface ResponseListener extends MessageListener{ + + /** + * 消息响应超时处理方法 + * @param responseEvent + */ + default void timeout(ResponseEvent responseEvent) {} + + /** + * 消息响应处理方法,响应内容与对应的请求内容封装在ResponseEvent中 + * @param responseEvent + */ + default void response(ResponseEvent responseEvent) {} +} diff --git a/nat-core/src/main/java/core/netty/group/channel/message/sender/MessageSender.java b/nat-core/src/main/java/core/netty/group/channel/message/sender/MessageSender.java new file mode 100644 index 0000000..c5f7668 --- /dev/null +++ b/nat-core/src/main/java/core/netty/group/channel/message/sender/MessageSender.java @@ -0,0 +1,9 @@ +package core.netty.group.channel.message.sender; + +/** + * @author wneck130@gmail.com + * @Description: + * @date 2021/10/11 + */ +public interface MessageSender { +} diff --git a/nat-core/src/main/java/core/netty/group/channel/strategy/ForkStrategy.java b/nat-core/src/main/java/core/netty/group/channel/strategy/ForkStrategy.java new file mode 100644 index 0000000..226ca32 --- /dev/null +++ b/nat-core/src/main/java/core/netty/group/channel/strategy/ForkStrategy.java @@ -0,0 +1,14 @@ +package core.netty.group.channel.strategy; + +import io.netty.channel.Channel; +import io.netty.channel.group.ChannelGroup; + +/** + * @author wneck130@gmail.com + * @Description: + * @date 2021/9/28 + */ +public interface ForkStrategy { + + Channel fork(ChannelGroup channelGroup); +} diff --git a/nat-core/src/main/java/core/netty/group/channel/strategy/KeyBasedForkStrategy.java b/nat-core/src/main/java/core/netty/group/channel/strategy/KeyBasedForkStrategy.java new file mode 100644 index 0000000..6439641 --- /dev/null +++ b/nat-core/src/main/java/core/netty/group/channel/strategy/KeyBasedForkStrategy.java @@ -0,0 +1,28 @@ +package core.netty.group.channel.strategy; + +import io.netty.channel.Channel; +import io.netty.channel.group.ChannelGroup; +import lombok.Setter; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author wneck130@gmail.com + * @Description: + * @date 2021/9/28 + */ +@Setter +public class KeyBasedForkStrategy implements ForkStrategy{ + + private String key; + + @Override + public Channel fork(ChannelGroup channelGroup) { + if (channelGroup == null || channelGroup.size() == 0) { + return null; + } + List list = new ArrayList<>(channelGroup); + return list.get(Math.abs(key.hashCode()%channelGroup.size())); + } +} diff --git a/nat-core/src/main/java/core/netty/group/channel/strategy/MinLoadForkStrategy.java b/nat-core/src/main/java/core/netty/group/channel/strategy/MinLoadForkStrategy.java new file mode 100644 index 0000000..a33ef2c --- /dev/null +++ b/nat-core/src/main/java/core/netty/group/channel/strategy/MinLoadForkStrategy.java @@ -0,0 +1,21 @@ +package core.netty.group.channel.strategy; + +import io.netty.channel.Channel; +import io.netty.channel.group.ChannelGroup; + +import java.util.Comparator; + +/** + * @author wneck130@gmail.com + * @Description: + * @date 2021/9/28 + */ +public class MinLoadForkStrategy implements ForkStrategy{ + @Override + public Channel fork(ChannelGroup channelGroup) { + if (channelGroup == null) { + return null; + } + return channelGroup.stream().min(Comparator.comparingLong(Channel::bytesBeforeWritable)).get(); + } +} diff --git a/nat-core/src/main/java/core/netty/group/channel/strategy/RandomForkStrategy.java b/nat-core/src/main/java/core/netty/group/channel/strategy/RandomForkStrategy.java new file mode 100644 index 0000000..d9f0c35 --- /dev/null +++ b/nat-core/src/main/java/core/netty/group/channel/strategy/RandomForkStrategy.java @@ -0,0 +1,25 @@ +package core.netty.group.channel.strategy; + +import io.netty.channel.Channel; +import io.netty.channel.group.ChannelGroup; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +/** + * @author wneck130@gmail.com + * @Description: + * @date 2021/9/28 + */ +public class RandomForkStrategy implements ForkStrategy { + + @Override + public Channel fork(ChannelGroup channelGroup) { + if (channelGroup == null) { + return null; + } + List list = new ArrayList<>(channelGroup); + return list.get(new Random().nextInt(list.size())); + } +} diff --git a/nat-core/src/main/java/core/netty/group/channel/strategy/RoundRobinForkStrategy.java b/nat-core/src/main/java/core/netty/group/channel/strategy/RoundRobinForkStrategy.java new file mode 100644 index 0000000..6612b43 --- /dev/null +++ b/nat-core/src/main/java/core/netty/group/channel/strategy/RoundRobinForkStrategy.java @@ -0,0 +1,31 @@ +package core.netty.group.channel.strategy; + +import io.netty.channel.Channel; +import io.netty.channel.group.ChannelGroup; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author wneck130@gmail.com + * @Description: + * @date 2021/9/28 + */ +public class RoundRobinForkStrategy implements ForkStrategy { + + private static AtomicInteger lastIndex = new AtomicInteger(0); + + @Override + public Channel fork(ChannelGroup channelGroup) { + if (channelGroup == null) { + return null; + } + if (lastIndex.get() > channelGroup.size()) { + lastIndex = new AtomicInteger(0); + } + List list = new ArrayList<>(channelGroup); + return list.get(new Random().nextInt(lastIndex.getAndIncrement())); + } +} diff --git a/nat-core/src/main/java/core/netty/group/channel/strategy/StrategyManager.java b/nat-core/src/main/java/core/netty/group/channel/strategy/StrategyManager.java new file mode 100644 index 0000000..2402a93 --- /dev/null +++ b/nat-core/src/main/java/core/netty/group/channel/strategy/StrategyManager.java @@ -0,0 +1,41 @@ +package core.netty.group.channel.strategy; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author wneck130@gmail.com + * @Description: + * @date 2021/9/28 + */ +public class StrategyManager { + + private static Map, ForkStrategy> instances = new ConcurrentHashMap<>(); + + /** + * @param clazz + * @return + * @throws InstantiationException + * @throws IllegalAccessException + */ + @SuppressWarnings("unchecked") + public static ForkStrategy getInstance(Class clazz) { + Object instance = instances.get(clazz); + if (instance == null) { + synchronized (StrategyManager.class) { + instance = instances.get(clazz); + if (instance == null) { + try { + instance = clazz.newInstance(); + } catch (InstantiationException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + instances.put(clazz, (ForkStrategy) instance); + } + } + } + return (ForkStrategy) instance; + } +} diff --git a/nat-core/src/main/java/core/netty/group/channel/strategy/constant/ForkStrategyEnum.java b/nat-core/src/main/java/core/netty/group/channel/strategy/constant/ForkStrategyEnum.java new file mode 100644 index 0000000..03722cd --- /dev/null +++ b/nat-core/src/main/java/core/netty/group/channel/strategy/constant/ForkStrategyEnum.java @@ -0,0 +1,36 @@ +package core.netty.group.channel.strategy.constant; + +import core.netty.group.channel.strategy.KeyBasedForkStrategy; +import core.netty.group.channel.strategy.MinLoadForkStrategy; +import core.netty.group.channel.strategy.RandomForkStrategy; +import core.netty.group.channel.strategy.RoundRobinForkStrategy; +import lombok.Getter; + +/** + * @author wneck130@gmail.com + * @Description: + * @date 2021/9/28 + */ +@Getter +public enum ForkStrategyEnum { + + RANDOM("随机获取", RandomForkStrategy.class), + MIN_LOAD("最小负载", MinLoadForkStrategy.class), + ROUND_ROBIN("轮询", RoundRobinForkStrategy.class), + KEY("根据Key获取", KeyBasedForkStrategy.class); + + /** + * 策略名 + */ + private String name; + + /** + * 策略实现类 + */ + private Class clazz; + + ForkStrategyEnum (String name, Class clazz) { + this.name = name; + this.clazz = clazz; + } +} diff --git a/nat-core/src/main/java/core/netty/handler/DispatcherHandler.java b/nat-core/src/main/java/core/netty/handler/DispatcherHandler.java new file mode 100644 index 0000000..28455ec --- /dev/null +++ b/nat-core/src/main/java/core/netty/handler/DispatcherHandler.java @@ -0,0 +1,77 @@ +package core.netty.handler; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ByteToMessageDecoder; +import lombok.extern.slf4j.Slf4j; + +import java.util.List; + +/** + * @author wneck130@gmail.com + * @Description: 协议分发控制器,根据收到的首包消息内容进行判断,区分TCP和HTTP协议 + * @date 2021/9/26 + */ +@Slf4j +public abstract class DispatcherHandler extends ByteToMessageDecoder { + + @Override + protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List list) throws Exception { + // Will use the first five bytes to detect a protocol. + if (byteBuf.readableBytes() < 5) { + return; + } + final int magic1 = byteBuf.getUnsignedByte(byteBuf.readerIndex()); + final int magic2 = byteBuf.getUnsignedByte(byteBuf.readerIndex() + 1); + + // 判断是不是HTTP请求 + if (isHttp(magic1, magic2)) { + log.info("this is a http msg"); + addHttpHandler(channelHandlerContext); + } else { + log.info("this is a socket msg"); +// addTcpHandler(channelHandlerContext); + } + channelHandlerContext.pipeline().remove(this); + } + + /** + * 配置http请求的pipeline + * @param ctx + */ + public abstract void addHttpHandler(ChannelHandlerContext ctx); + + /** + * 配置Tcp请求的pipeline + * @param ctx + */ + public abstract void addTcpHandler(ChannelHandlerContext ctx); + + /** + * 判断请求是否是HTTP请求 + * + * @param magic1 报文第一个字节 + * @param magic2 报文第二个字节 + * @return + */ + public boolean isHttp(int magic1, int magic2) { + // GET + return magic1 == 'G' && magic2 == 'E' || + // POST + magic1 == 'P' && magic2 == 'O' || + // PUT + magic1 == 'P' && magic2 == 'U' || + // HEAD + magic1 == 'H' && magic2 == 'E' || + // OPTIONS + magic1 == 'O' && magic2 == 'P' || + // PATCH + magic1 == 'P' && magic2 == 'A' || + // DELETE + magic1 == 'D' && magic2 == 'E' || + // TRACE + magic1 == 'T' && magic2 == 'R' || + // CONNECT + magic1 == 'C' && magic2 == 'O'; + } +} diff --git a/nat-core/src/main/java/core/netty/handler/MessageReceiveFilter.java b/nat-core/src/main/java/core/netty/handler/MessageReceiveFilter.java new file mode 100644 index 0000000..9d08613 --- /dev/null +++ b/nat-core/src/main/java/core/netty/handler/MessageReceiveFilter.java @@ -0,0 +1,40 @@ +package core.netty.handler; + +import core.constant.FrameConstant; +import core.entity.Frame; +import core.netty.group.channel.message.MessageContext; +import core.netty.group.channel.message.ResponseEvent; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import lombok.extern.slf4j.Slf4j; + +/** + * @author wneck130@gmail.com + * @Description: 消息响应过滤器,推送消息响应事件 + * @date 2021/9/28 + */ +@Slf4j +public class MessageReceiveFilter extends SimpleChannelInboundHandler { + + @Override + protected void channelRead0(ChannelHandlerContext ctx, Frame msg) throws Exception { + try { + //服务端主动发送的消息才需要监听响应 + if (msg.getPv() == FrameConstant.RES_PV) { + //生成事件源 + MessageContext messageContext = MessageContext.getInstance(); + //生成对应的responseEvent + String channelId = msg.getReq(); + ResponseEvent responseEvent = new ResponseEvent(messageContext); + responseEvent.setChannelId(channelId); + responseEvent.setRequest(MessageContext.getHistoryFrame(channelId, msg.getSerial())); + responseEvent.setResponse(msg); + messageContext.notifyResponse(responseEvent); + return; + } + ctx.fireChannelRead(msg); + } catch (Exception e) { + log.error("{}\n生成响应消息事件失败", msg.toString(), e); + } + } +} diff --git a/nat-core/src/main/java/core/netty/handler/MessageSendFilter.java b/nat-core/src/main/java/core/netty/handler/MessageSendFilter.java new file mode 100644 index 0000000..0da66a9 --- /dev/null +++ b/nat-core/src/main/java/core/netty/handler/MessageSendFilter.java @@ -0,0 +1,26 @@ +package core.netty.handler; + +import core.constant.FrameConstant; +import core.entity.Frame; +import core.netty.group.channel.message.MessageContext; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelOutboundHandlerAdapter; +import io.netty.channel.ChannelPromise; + +/** + * @author wneck130@gmail.com + * @Description: 消息请求过滤器 + * 每一条发送的消息存储至 {@link core.netty.group.channel.message.MessageContext}的历史消息中等待响应 + * @date 2021/9/30 + */ +public class MessageSendFilter extends ChannelOutboundHandlerAdapter { + + @Override + public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { + Frame frame = (Frame) msg; + if (frame.getPv() == FrameConstant.REQ_PV) { + MessageContext.addHistoryFrame(frame.getReq(), frame); + } + super.write(ctx, msg, promise); + } +} diff --git a/nat-core/src/main/java/core/netty/handler/processor/Processor.java b/nat-core/src/main/java/core/netty/handler/processor/Processor.java new file mode 100644 index 0000000..418c544 --- /dev/null +++ b/nat-core/src/main/java/core/netty/handler/processor/Processor.java @@ -0,0 +1,12 @@ +package core.netty.handler.processor; + +import core.netty.group.channel.message.receiver.listener.RequestListener; +import core.netty.group.channel.message.receiver.listener.ResponseListener; + +/** + * @Author wneck130@gmail.com + * @function + */ +public interface Processor extends RequestListener, ResponseListener { + +} diff --git a/nat-core/src/main/java/core/netty/handler/processor/ProcessorManager.java b/nat-core/src/main/java/core/netty/handler/processor/ProcessorManager.java new file mode 100644 index 0000000..198e130 --- /dev/null +++ b/nat-core/src/main/java/core/netty/handler/processor/ProcessorManager.java @@ -0,0 +1,41 @@ +package core.netty.handler.processor; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author wneck130@gmail.com + * @Description: + * @date 2021/9/24 + */ +public class ProcessorManager { + + private static Map instances = new ConcurrentHashMap<>(); + + /** + * @param clazz + * @return + * @throws InstantiationException + * @throws IllegalAccessException + */ + @SuppressWarnings("unchecked") + public static Processor getInstance(Class clazz) { + Object instance = instances.get(clazz); + if (instance == null) { + synchronized (ProcessorManager.class) { + instance = instances.get(clazz); + if (instance == null) { + try { + instance = clazz.newInstance(); + } catch (InstantiationException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + instances.put(clazz, (Processor) instance); + } + } + } + return (Processor) instance; + } +} diff --git a/nat-client/src/main/java/client/BaseClient.java b/nat-core/src/main/java/core/netty/stater/client/BaseClient.java similarity index 82% rename from nat-client/src/main/java/client/BaseClient.java rename to nat-core/src/main/java/core/netty/stater/client/BaseClient.java index 76e0ec7..4190cdd 100644 --- a/nat-client/src/main/java/client/BaseClient.java +++ b/nat-core/src/main/java/core/netty/stater/client/BaseClient.java @@ -1,7 +1,10 @@ -package client; +package core.netty.stater.client; -import core.cache.PropertiesCache; +import core.properties.cache.PropertiesCache; +import io.netty.channel.ChannelFuture; import io.netty.channel.EventLoopGroup; +import lombok.Getter; +import lombok.Setter; import lombok.extern.slf4j.Slf4j; import java.util.concurrent.*; @@ -11,18 +14,24 @@ * @function 客户端基类 */ @Slf4j +@Getter +@Setter public class BaseClient { protected EventLoopGroup group; protected PropertiesCache cache; private static int corePoolSize = Runtime.getRuntime().availableProcessors() * 2 + 1; private static int maximumPoolSize = Runtime.getRuntime().availableProcessors() * 3; - private static int keepAliveTime = 10; + private static int keepAliveTime = 30; public static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS, new LinkedBlockingQueue<>(100), new ThreadPoolExecutor.DiscardOldestPolicy()); public static ScheduledExecutorService scheduledExecutor = Executors.newSingleThreadScheduledExecutor(); + public ChannelFuture future; + + protected String host; + protected Integer port; protected void doShutdown(){ try{ if(group != null){ diff --git a/nat-core/src/main/java/core/netty/stater/client/NettyClient.java b/nat-core/src/main/java/core/netty/stater/client/NettyClient.java new file mode 100644 index 0000000..1500dc3 --- /dev/null +++ b/nat-core/src/main/java/core/netty/stater/client/NettyClient.java @@ -0,0 +1,13 @@ +package core.netty.stater.client; + +/** + * @author wneck130@gmail.com + * @Description: + * @date 2021/9/26 + */ +public interface NettyClient { + + void init(); + + void start(); +} diff --git a/nat-core/src/main/java/core/netty/stater/factory/InternalStaterFactory.java b/nat-core/src/main/java/core/netty/stater/factory/InternalStaterFactory.java new file mode 100644 index 0000000..67b3bfd --- /dev/null +++ b/nat-core/src/main/java/core/netty/stater/factory/InternalStaterFactory.java @@ -0,0 +1,21 @@ +package core.netty.stater.factory; + +import core.netty.stater.client.NettyClient; +import core.netty.stater.server.NettyServer; + +/** + * @author wneck130@gmail.com + * @Description: + * @date 2021/9/26 + */ +public class InternalStaterFactory implements StaterFactory { + @Override + public NettyServer getServer() { + return null; + } + + @Override + public NettyClient getClient() { + return null; + } +} diff --git a/nat-core/src/main/java/core/netty/stater/factory/ProxyStaterFactory.java b/nat-core/src/main/java/core/netty/stater/factory/ProxyStaterFactory.java new file mode 100644 index 0000000..2d4b85c --- /dev/null +++ b/nat-core/src/main/java/core/netty/stater/factory/ProxyStaterFactory.java @@ -0,0 +1,22 @@ +package core.netty.stater.factory; + +import core.netty.stater.client.NettyClient; +import core.netty.stater.server.NettyServer; + +/** + * @author wneck130@gmail.com + * @Description: + * @date 2021/9/26 + */ +public class ProxyStaterFactory implements StaterFactory { + + @Override + public NettyServer getServer() { + return null; + } + + @Override + public NettyClient getClient() { + return null; + } +} diff --git a/nat-core/src/main/java/core/netty/stater/factory/StaterFactory.java b/nat-core/src/main/java/core/netty/stater/factory/StaterFactory.java new file mode 100644 index 0000000..8125ef6 --- /dev/null +++ b/nat-core/src/main/java/core/netty/stater/factory/StaterFactory.java @@ -0,0 +1,17 @@ +package core.netty.stater.factory; + +import core.netty.stater.client.NettyClient; +import core.netty.stater.server.NettyServer; + +/** + * @author wneck130@gmail.com + * @Description: + * @date 2021/9/26 + */ +public interface StaterFactory { + + NettyServer getServer(); + + NettyClient getClient(); + +} diff --git a/nat-server/src/main/java/server/BaseServer.java b/nat-core/src/main/java/core/netty/stater/server/BaseServer.java similarity index 64% rename from nat-server/src/main/java/server/BaseServer.java rename to nat-core/src/main/java/core/netty/stater/server/BaseServer.java index 556809c..da71da5 100644 --- a/nat-server/src/main/java/server/BaseServer.java +++ b/nat-core/src/main/java/core/netty/stater/server/BaseServer.java @@ -1,8 +1,10 @@ -package server; +package core.netty.stater.server; -import core.cache.PropertiesCache; +import core.properties.cache.PropertiesCache; import io.netty.channel.ChannelFuture; import io.netty.channel.EventLoopGroup; +import io.netty.util.concurrent.GenericFutureListener; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; import java.util.concurrent.*; @@ -12,17 +14,47 @@ * @Function 服务端基类 */ @Slf4j +@Getter public abstract class BaseServer { - public static ScheduledExecutorService scheduledExecutor = Executors.newSingleThreadScheduledExecutor(); + /** + * 全局异步任务线程池 + */ private static int corePoolSize = Runtime.getRuntime().availableProcessors() * 2 + 1; private static int maximumPoolSize = Runtime.getRuntime().availableProcessors() * 3; private static int keepAliveTime = 10; public static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS, new LinkedBlockingQueue<>(100), new ThreadPoolExecutor.DiscardOldestPolicy()); + + /** + * boss线程组 + */ protected EventLoopGroup bossGroup; + /** + * worker线程组 + */ protected EventLoopGroup workerGroup; - protected ChannelFuture f; + /** + * NioServerSocketChannel对应的future + */ + protected ChannelFuture nioServerFuture; + + /** + * 通道读写超时配置项 + */ + protected final int READ_IDLE = 0; + protected final int WRITE_IDLE = 0; + protected final int ALL_IDLE = 10; + + /** + * 代理程序对外提供的服务端口 + */ + protected static String PORT = "proxy.server.port"; + + protected int port; + + protected GenericFutureListener genericFutureListener; + protected PropertiesCache cache; protected void doShutdown() { @@ -48,9 +80,12 @@ protected void addShutdownHook() { runtime.addShutdownHook(new Thread() { @Override public void run() { - log.debug("执行 addShutdownHook..."); + log.info("执行 addShutdownHook..."); doShutdown(); + close(); } }); } + + public abstract void close(); } diff --git a/nat-core/src/main/java/core/netty/stater/server/NettyServer.java b/nat-core/src/main/java/core/netty/stater/server/NettyServer.java new file mode 100644 index 0000000..e1edbe9 --- /dev/null +++ b/nat-core/src/main/java/core/netty/stater/server/NettyServer.java @@ -0,0 +1,17 @@ +package core.netty.stater.server; + +/** + * @author wneck130@gmail.com + * @Description: + * @date 2021/9/26 + */ +public interface NettyServer { + + void init(); + + void start(int port); + + boolean isHeathy(); + + boolean isRunning(); +} diff --git a/nat-core/src/main/java/core/processor/Processor.java b/nat-core/src/main/java/core/processor/Processor.java deleted file mode 100644 index c516e73..0000000 --- a/nat-core/src/main/java/core/processor/Processor.java +++ /dev/null @@ -1,20 +0,0 @@ -package core.processor; - -import core.entity.Frame; -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; - -/** - * @Author wneck130@gmail.com - * @function - */ -public interface Processor { - - /** - * Frame消息处理器接口 - * @param ctx - * @param msg - * @throws Exception - */ - void process(ChannelHandlerContext ctx, Frame msg) throws Exception; -} diff --git a/nat-core/src/main/java/core/properties/cache/PropertiesCache.java b/nat-core/src/main/java/core/properties/cache/PropertiesCache.java new file mode 100644 index 0000000..a536e41 --- /dev/null +++ b/nat-core/src/main/java/core/properties/cache/PropertiesCache.java @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2017 hadlinks, All Rights Reserved. + */ +package core.properties.cache; + +import java.util.List; +import java.util.concurrent.*; + +import core.utils.ByteUtil; + +/** + * Project Name: huamei-farm + * Package Name: core.properties.cache + * ClassName: PropertiesCache + * Function: 缓存项目的全局配置信息. + * date: 2017/3/15 10:53 + * + * @author songwei (songw@hadlinks.com) + * @since JDK 1.8 + */ +public class PropertiesCache { + + /** + * 私有的空参构造函数 + */ + private PropertiesCache() { + } + + /** + * 单例 + * + * @return + */ + public static PropertiesCache getInstance() { + return Holder.INSTANCE; + } + + /** + * 懒加载 + * + * @author songw + */ + private static class Holder { + final static PropertiesCache INSTANCE = new PropertiesCache(); + } + + private final ConcurrentMap props = new ConcurrentHashMap<>(); + + + public ConcurrentMap getProps() { + return props; + } + + public String get(String key) { + return props.get(key).toString(); + } + + public Integer getInt(String key) { + return Integer.valueOf(this.get(key)); + } + + public Byte getByte(String key) { + return ByteUtil.parseHexString(this.get(key)); + } + + public byte[] getBytes(String key) { + return ByteUtil.parseHexStringToArray(this.get(key)); + } + + public List getList(String key) { + return (List) props.get(key); + } +} diff --git a/nat-core/src/main/java/core/frame/filewatch/FileWatchService.java b/nat-core/src/main/java/core/properties/filewatch/FileWatchService.java similarity index 94% rename from nat-core/src/main/java/core/frame/filewatch/FileWatchService.java rename to nat-core/src/main/java/core/properties/filewatch/FileWatchService.java index 2231d6d..a593b6f 100644 --- a/nat-core/src/main/java/core/frame/filewatch/FileWatchService.java +++ b/nat-core/src/main/java/core/properties/filewatch/FileWatchService.java @@ -6,9 +6,8 @@ * Copyright (c) 2017, hadlinks All Rights Reserved. * */ -package core.frame.filewatch; +package core.properties.filewatch; -import java.io.File; import java.nio.file.FileSystems; import java.nio.file.Path; import java.nio.file.Paths; @@ -18,7 +17,7 @@ import java.nio.file.WatchKey; import java.nio.file.WatchService; import org.apache.logging.log4j.*; -import core.frame.loader.AbstractLoader; +import core.properties.loader.AbstractLoader; /** * ClassName: FileWatchService diff --git a/nat-core/src/main/java/core/frame/loader/AbstractLoader.java b/nat-core/src/main/java/core/properties/loader/AbstractLoader.java similarity index 96% rename from nat-core/src/main/java/core/frame/loader/AbstractLoader.java rename to nat-core/src/main/java/core/properties/loader/AbstractLoader.java index b8a6b71..c86c407 100644 --- a/nat-core/src/main/java/core/frame/loader/AbstractLoader.java +++ b/nat-core/src/main/java/core/properties/loader/AbstractLoader.java @@ -6,7 +6,7 @@ * Copyright (c) 2017, hadlinks All Rights Reserved. * */ -package core.frame.loader; +package core.properties.loader; /** * ClassName: AbstructLoader diff --git a/nat-core/src/main/java/core/frame/loader/PropertiesLoader.java b/nat-core/src/main/java/core/properties/loader/PropertiesLoader.java similarity index 63% rename from nat-core/src/main/java/core/frame/loader/PropertiesLoader.java rename to nat-core/src/main/java/core/properties/loader/PropertiesLoader.java index 0c20af8..7d33d9a 100644 --- a/nat-core/src/main/java/core/frame/loader/PropertiesLoader.java +++ b/nat-core/src/main/java/core/properties/loader/PropertiesLoader.java @@ -4,9 +4,8 @@ * Package Name: core.frame.loader * Date: 2017年3月20日上午10:47:55 * Copyright (c) 2017, hadlinks All Rights Reserved. - * */ -package core.frame.loader; +package core.properties.loader; import java.io.File; import java.io.FileInputStream; @@ -16,8 +15,8 @@ import org.apache.logging.log4j.*; -import core.cache.PropertiesCache; -import core.frame.filewatch.FileWatchService; +import core.properties.cache.PropertiesCache; +import core.properties.filewatch.FileWatchService; /** * ClassName: PropertiesLoader @@ -28,12 +27,12 @@ * @version * @since JDK 1.8 */ -public class PropertiesLoader extends AbstractLoader{ +public class PropertiesLoader extends AbstractLoader { - private final Logger logger = LogManager.getLogger(PropertiesLoader.class); + private final Logger logger = LogManager.getLogger(PropertiesLoader.class); - @Override - public void load(String path) throws Exception { + @Override + public void load(String path) throws Exception { Properties properties = new Properties(); //读取文件 File propFile = readFiles(path); @@ -47,45 +46,40 @@ public void load(String path) throws Exception { } //加载完成后,启动一个FileWatchService来对文件修改状态进行监控,如果监听到文件修改,则重新加载修改后的文件内容 addWatch(propFile.getParent()); - } + } - @Override - public void reload(String path) { - try{ - Properties properties = new Properties(); - //读取文件 - File propFile = readFiles(path); - if (!propFile.exists()) { - return; - } - FileInputStream in = new FileInputStream(propFile); - properties.load(in); - for(Object key : properties.keySet()){ - PropertiesCache.getInstance().getProps().putIfAbsent(key.toString(), properties.get(key).toString()); - } - }catch(Exception e){ - e.printStackTrace(); - logger.error(e); - } - } + @Override + public void reload(String path) throws Exception { + Properties properties = new Properties(); + //读取文件 + File propFile = readFiles(path); + if (!propFile.exists()) { + return; + } + FileInputStream in = new FileInputStream(propFile); + properties.load(in); + for (Object key : properties.keySet()) { + PropertiesCache.getInstance().getProps().putIfAbsent(key.toString(), properties.get(key).toString()); + } + } - /** - * 添加监控 - * @param path - */ - public void addWatch(String path) { - FileWatchService service = new FileWatchService(); - new Thread(() -> service.addWatcher(path, this)).start(); - } + /** + * 添加监控 + * @param path + */ + public void addWatch(String path) { + FileWatchService service = new FileWatchService(); + new Thread(() -> service.addWatcher(path, this)).start(); + } - /** - * 读取指定路径下的文件,按文件层级由浅到深返回第一个匹配的properties文件 - * @param path - * @return - */ - public File readFiles(String path) { - try{ - File dir = new File(path); + /** + * 读取指定路径下的文件,按文件层级由浅到深返回第一个匹配的properties文件 + * @param path + * @return + */ + public File readFiles(String path) { + try { + File dir = new File(path); List subFiles = Stream.of(dir.listFiles()).filter((file) -> file.isFile()).collect(Collectors.toList()); if (!subFiles.isEmpty()) { Optional optional = subFiles.stream().filter((file) -> file.getName().equalsIgnoreCase("properties.properties")).findFirst(); @@ -106,10 +100,10 @@ public File readFiles(String path) { } } } - }catch(Exception e){ - e.printStackTrace(); - } - return null; - } + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } } diff --git a/nat-core/src/main/java/core/properties/loader/YamlLoader.java b/nat-core/src/main/java/core/properties/loader/YamlLoader.java new file mode 100644 index 0000000..d54e79c --- /dev/null +++ b/nat-core/src/main/java/core/properties/loader/YamlLoader.java @@ -0,0 +1,114 @@ +package core.properties.loader; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import core.properties.cache.PropertiesCache; +import core.properties.filewatch.FileWatchService; +import lombok.extern.slf4j.Slf4j; + +import java.io.File; +import java.io.FileInputStream; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * @author wneck130@gmail.com + * @Description: + * @date 2021/9/30 + */ +@Slf4j +public class YamlLoader extends AbstractLoader { + + private static final String EXTENDTION = "."; + + @Override + public void load(String path) throws Exception { + //读取文件 + File propFile = readFiles(path); + if (propFile == null) { + throw new Exception("无法加载配置文件!!!"); + } + ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); + Map origin = mapper.readValue(propFile, Map.class); + unfold(origin).forEach((key, value) -> { + PropertiesCache.getInstance().getProps().putIfAbsent(key, value); + }); + //加载完成后,启动一个FileWatchService来对文件修改状态进行监控,如果监听到文件修改,则重新加载修改后的文件内容 + addWatch(propFile.getParent()); + } + + @Override + public void reload(String path) throws Exception { + //读取文件 + File propFile = readFiles(path); + if (propFile == null) { + throw new Exception("无法加载配置文件!!!"); + } + ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); + Map origin = mapper.readValue(new File(path), Map.class); + unfold(origin).forEach((key, value) -> { + PropertiesCache.getInstance().getProps().putIfAbsent(key, value); + }); + } + + /** + * 添加监控 + * @param path + */ + public void addWatch(String path) { + FileWatchService service = new FileWatchService(); + new Thread(() -> service.addWatcher(path, this)).start(); + } + + public Map unfold(Map origin) { + Map target = new HashMap<>(); + origin.forEach((key, value) -> { + String prefix = key.toString(); + // + if (value instanceof LinkedHashMap) { + Map subMapOrigin = unfold((Map)value); + target.putAll(subMapOrigin.entrySet().stream().collect(Collectors.toMap( + stringObjectEntry -> prefix+ EXTENDTION+stringObjectEntry.getKey(), + stringObjectEntry -> stringObjectEntry.getValue()))); + } else { + target.put(key, value); + } + }); + return target; + } + + /** + * 读取指定路径下的文件,按文件层级由浅到深返回第一个匹配的properties.yml文件 + * @param path + * @return + */ + public File readFiles(String path) { + try{ + File dir = new File(path); + List subFiles = Stream.of(dir.listFiles()).filter(File::isFile).collect(Collectors.toList()); + if (!subFiles.isEmpty()) { + Optional optional = subFiles.stream().filter((file) -> file.getName().equalsIgnoreCase("properties.yml")).findFirst(); + //当前目录下有需要的文件,直接返回 + if (optional.isPresent()) { + log.debug("找到配置文件:" + optional.get().getAbsolutePath()); + return optional.get(); + } + } + //当前目录下没有,则查找子目录 + List subDirs = Stream.of(dir.listFiles()).filter(File::isDirectory).collect(Collectors.toList()); + if (!subDirs.isEmpty()) { + for (File subDir : subDirs) { + File subFile = readFiles(subDir.getPath()); + if (subFile != null) { + log.debug("找到配置文件:" + subFile.getAbsolutePath()); + return subFile; + } + } + } + }catch(Exception e){ + e.printStackTrace(); + } + return null; + } +} diff --git a/nat-core/src/main/java/core/singleton/AbstractSingleton.java b/nat-core/src/main/java/core/singleton/AbstractSingleton.java deleted file mode 100644 index 0cf1b7c..0000000 --- a/nat-core/src/main/java/core/singleton/AbstractSingleton.java +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Copyright (c) 2017 hadlinks, All Rights Reserved. - */ -package core.singleton; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import java.io.Serializable; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -/** - * Project Name: xingxing-farm - * Package Name: core.singleton - * ClassName: AbstractSingleton - * Function: 项目中所有单例对象的统一父类,模拟懒加载的形式,只在首次调用时创建对象. - * date: 2017/10/14 14:41 - * @author songwei (songw@hadlinks.com) - * @since JDK 1.8 - */ -public abstract class AbstractSingleton implements Serializable { - - private static final ConcurrentMap CLASS = new ConcurrentHashMap<>(); - private static final long serialVersionUID = -8691344637857430181L; - private static final Logger logger = LogManager.getLogger(AbstractSingleton.class); - - /** - * 限制性构造函数 - * 所有AbstractSingleton的子类除首次实例创建外,不允许使用new关键字进行实例创建,如在实例已经存在时使用则抛出异常。 - * 并发情况下,锁表后进行进一步判断,防止多线程创建时出现问题。 - * @throws Exception - */ - AbstractSingleton () throws Exception { - String clazzName = this.getClass().getName(); - if (CLASS.containsKey(clazzName)) { - throw new Exception("Cannot construct instance for singleton class " + clazzName - + ", cause an instance has existed !"); - } else { - synchronized (CLASS) { - if (CLASS.containsKey(clazzName)) { - throw new Exception("Cannot construct instance for singleton class " + clazzName - + ", cause an instance has existed !"); - } else { - CLASS.putIfAbsent(clazzName, this); - } - } - } - } - - /** - * 返回一个AbstractSingleton子类的单例对象实例 - * 如果首次调用,则创建实例并缓存在classMap中,其余时候调用均返回classMap中缓存的实例对象,模拟懒汉模式 - * @param clazz - * @param - * @return - */ - public static T newInstance(Class clazz) { - String className = clazz.getName(); - if (!CLASS.containsKey(className)) { - try { - T instatnce = clazz.newInstance(); - CLASS.putIfAbsent(clazz.getName(), instatnce); - return instatnce; - } catch (Exception e) { - logger.error(e); - return null; - } - } else { - return (T)CLASS.get(className); - } - } -} diff --git a/nat-core/src/main/java/core/factory/SslContextFactory.java b/nat-core/src/main/java/core/ssl/factory/SslContextFactory.java similarity index 98% rename from nat-core/src/main/java/core/factory/SslContextFactory.java rename to nat-core/src/main/java/core/ssl/factory/SslContextFactory.java index 5fcf4a2..97c5706 100644 --- a/nat-core/src/main/java/core/factory/SslContextFactory.java +++ b/nat-core/src/main/java/core/ssl/factory/SslContextFactory.java @@ -1,7 +1,7 @@ /** * Copyright (c) 2017 hadlinks, All Rights Reserved. */ -package core.factory; +package core.ssl.factory; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; diff --git a/nat-core/target/maven-archiver/pom.properties b/nat-core/target/maven-archiver/pom.properties new file mode 100644 index 0000000..0c43e19 --- /dev/null +++ b/nat-core/target/maven-archiver/pom.properties @@ -0,0 +1,5 @@ +#Generated by Maven +#Wed Oct 13 16:09:18 CST 2021 +version=1.0-SNAPSHOT +groupId=com.scrat +artifactId=nat-core diff --git a/nat-core/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/nat-core/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file mode 100644 index 0000000..1dbc8fb --- /dev/null +++ b/nat-core/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -0,0 +1,51 @@ +core\netty\group\channel\strategy\constant\ForkStrategyEnum.class +core\netty\group\channel\message\receiver\listener\MessageListener.class +core\netty\group\channel\strategy\RoundRobinForkStrategy.class +core\constant\FrameConstant.class +core\utils\ArrayUtil.class +core\netty\stater\server\NettyServer.class +core\netty\handler\MessageReceiveFilter.class +core\crypto\PaddingBytes.class +core\netty\stater\client\NettyClient.class +core\properties\cache\PropertiesCache.class +core\utils\ThreadUtil.class +core\netty\handler\processor\ProcessorManager.class +core\netty\group\channel\message\MessageContext$1.class +core\netty\stater\server\BaseServer.class +core\utils\DateUtil.class +core\utils\StringUtil.class +core\netty\handler\DispatcherHandler.class +core\netty\group\channel\strategy\MinLoadForkStrategy.class +core\properties\cache\PropertiesCache$1.class +core\utils\BufUtil.class +core\netty\handler\MessageSendFilter.class +core\entity\Frame.class +core\entity\Tunnel.class +core\netty\group\channel\message\receiver\MessageReceiver.class +core\properties\loader\PropertiesLoader.class +core\properties\cache\PropertiesCache$Holder.class +core\properties\filewatch\FileWatchService.class +core\netty\stater\factory\StaterFactory.class +core\netty\group\channel\strategy\RandomForkStrategy.class +core\netty\group\ServerChannelGroup.class +core\netty\group\channel\message\ResponseEvent.class +core\netty\group\channel\strategy\KeyBasedForkStrategy.class +core\properties\loader\AbstractLoader.class +core\netty\stater\factory\InternalStaterFactory.class +core\netty\stater\server\BaseServer$1.class +core\netty\group\channel\message\MessageContext.class +core\netty\group\channel\strategy\ForkStrategy.class +core\netty\group\channel\strategy\StrategyManager.class +core\netty\handler\processor\Processor.class +core\properties\loader\YamlLoader.class +core\ssl\factory\SslContextFactory.class +core\netty\stater\factory\ProxyStaterFactory.class +core\crypto\Crypto.class +core\netty\group\channel\message\receiver\listener\RequestListener.class +core\netty\group\ClientChannelGroup.class +core\netty\group\channel\message\MessageContext$Holder.class +core\netty\group\channel\message\receiver\listener\ResponseListener.class +core\netty\stater\client\BaseClient.class +core\utils\ByteUtil.class +core\crypto\SimplePaddingBytes.class +core\netty\group\channel\message\sender\MessageSender.class diff --git a/nat-core/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/nat-core/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file mode 100644 index 0000000..ccb1e19 --- /dev/null +++ b/nat-core/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -0,0 +1,46 @@ +E:\workspace\netty-nat\nat-core\src\main\java\core\utils\ThreadUtil.java +E:\workspace\netty-nat\nat-core\src\main\java\core\netty\group\channel\strategy\RoundRobinForkStrategy.java +E:\workspace\netty-nat\nat-core\src\main\java\core\utils\ByteUtil.java +E:\workspace\netty-nat\nat-core\src\main\java\core\netty\stater\client\NettyClient.java +E:\workspace\netty-nat\nat-core\src\main\java\core\entity\Tunnel.java +E:\workspace\netty-nat\nat-core\src\main\java\core\netty\handler\DispatcherHandler.java +E:\workspace\netty-nat\nat-core\src\main\java\core\properties\cache\PropertiesCache.java +E:\workspace\netty-nat\nat-core\src\main\java\core\netty\group\channel\message\MessageContext.java +E:\workspace\netty-nat\nat-core\src\main\java\core\netty\stater\factory\StaterFactory.java +E:\workspace\netty-nat\nat-core\src\main\java\core\utils\DateUtil.java +E:\workspace\netty-nat\nat-core\src\main\java\core\netty\group\channel\message\ResponseEvent.java +E:\workspace\netty-nat\nat-core\src\main\java\core\netty\stater\server\NettyServer.java +E:\workspace\netty-nat\nat-core\src\main\java\core\netty\group\channel\strategy\StrategyManager.java +E:\workspace\netty-nat\nat-core\src\main\java\core\netty\group\ServerChannelGroup.java +E:\workspace\netty-nat\nat-core\src\main\java\core\netty\group\channel\strategy\constant\ForkStrategyEnum.java +E:\workspace\netty-nat\nat-core\src\main\java\core\ssl\factory\SslContextFactory.java +E:\workspace\netty-nat\nat-core\src\main\java\core\netty\handler\processor\Processor.java +E:\workspace\netty-nat\nat-core\src\main\java\core\netty\group\channel\message\receiver\listener\RequestListener.java +E:\workspace\netty-nat\nat-core\src\main\java\core\netty\group\channel\message\sender\MessageSender.java +E:\workspace\netty-nat\nat-core\src\main\java\core\netty\stater\factory\ProxyStaterFactory.java +E:\workspace\netty-nat\nat-core\src\main\java\core\crypto\PaddingBytes.java +E:\workspace\netty-nat\nat-core\src\main\java\core\crypto\Crypto.java +E:\workspace\netty-nat\nat-core\src\main\java\core\utils\BufUtil.java +E:\workspace\netty-nat\nat-core\src\main\java\core\netty\group\channel\message\receiver\MessageReceiver.java +E:\workspace\netty-nat\nat-core\src\main\java\core\netty\group\channel\strategy\KeyBasedForkStrategy.java +E:\workspace\netty-nat\nat-core\src\main\java\core\netty\group\channel\strategy\MinLoadForkStrategy.java +E:\workspace\netty-nat\nat-core\src\main\java\core\properties\loader\PropertiesLoader.java +E:\workspace\netty-nat\nat-core\src\main\java\core\netty\handler\MessageSendFilter.java +E:\workspace\netty-nat\nat-core\src\main\java\core\netty\group\ClientChannelGroup.java +E:\workspace\netty-nat\nat-core\src\main\java\core\netty\group\channel\strategy\RandomForkStrategy.java +E:\workspace\netty-nat\nat-core\src\main\java\core\netty\stater\server\BaseServer.java +E:\workspace\netty-nat\nat-core\src\main\java\core\netty\group\channel\strategy\ForkStrategy.java +E:\workspace\netty-nat\nat-core\src\main\java\core\utils\ArrayUtil.java +E:\workspace\netty-nat\nat-core\src\main\java\core\entity\Frame.java +E:\workspace\netty-nat\nat-core\src\main\java\core\netty\stater\client\BaseClient.java +E:\workspace\netty-nat\nat-core\src\main\java\core\netty\handler\processor\ProcessorManager.java +E:\workspace\netty-nat\nat-core\src\main\java\core\utils\StringUtil.java +E:\workspace\netty-nat\nat-core\src\main\java\core\netty\group\channel\message\receiver\listener\MessageListener.java +E:\workspace\netty-nat\nat-core\src\main\java\core\properties\loader\YamlLoader.java +E:\workspace\netty-nat\nat-core\src\main\java\core\constant\FrameConstant.java +E:\workspace\netty-nat\nat-core\src\main\java\core\netty\group\channel\message\receiver\listener\ResponseListener.java +E:\workspace\netty-nat\nat-core\src\main\java\core\netty\handler\MessageReceiveFilter.java +E:\workspace\netty-nat\nat-core\src\main\java\core\properties\filewatch\FileWatchService.java +E:\workspace\netty-nat\nat-core\src\main\java\core\properties\loader\AbstractLoader.java +E:\workspace\netty-nat\nat-core\src\main\java\core\crypto\SimplePaddingBytes.java +E:\workspace\netty-nat\nat-core\src\main\java\core\netty\stater\factory\InternalStaterFactory.java diff --git a/nat-core/target/nat-core-1.0-SNAPSHOT.jar b/nat-core/target/nat-core-1.0-SNAPSHOT.jar new file mode 100644 index 0000000..14aab9a Binary files /dev/null and b/nat-core/target/nat-core-1.0-SNAPSHOT.jar differ diff --git a/nat-server/pom.xml b/nat-server/pom.xml index fcdc83e..46fef0e 100644 --- a/nat-server/pom.xml +++ b/nat-server/pom.xml @@ -66,7 +66,7 @@ - + @@ -108,7 +108,7 @@ true libs/ - server.InternalServer + server.internal.InternalNettyServer diff --git a/nat-server/src/main/java/server/InternalServer.java b/nat-server/src/main/java/server/InternalServer.java deleted file mode 100644 index d32e520..0000000 --- a/nat-server/src/main/java/server/InternalServer.java +++ /dev/null @@ -1,79 +0,0 @@ -package server; - -import core.cache.PropertiesCache; -import core.constant.FrameConstant; -import core.frame.loader.PropertiesLoader; -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelOption; -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.channel.socket.SocketChannel; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.handler.codec.LengthFieldBasedFrameDecoder; -import io.netty.handler.timeout.IdleStateHandler; -import lombok.extern.slf4j.Slf4j; -import server.decoder.ByteToPojoDecoder; -import server.decoder.PojoToByteEncoder; -import server.group.ServerChannelGroup; -import server.handler.CustomEventHandler; -import server.handler.InternalServerHandler; - -import java.util.concurrent.TimeUnit; - -/** - * @Author wneck130@gmail.com - * @function internal服务端,用来接受程序内部的internalClient的连接,从而打通服务端与客户端的网络隔绝 - */ -@Slf4j -public class InternalServer extends BaseServer { - - /** - * 心跳超时时间,默认为2倍心跳发送间隔,心跳超时后服务主动回收连接 - */ - private static long HEARTBEAT_TIMEOUT = 2 * 1L; - - /** - * 代理程序对外开发的端口 - */ - private static String PORT = "internal.server.port"; - - public static void main(String[] args) throws Exception { - InternalServer internalServer = new InternalServer(); - internalServer.init(); - ServerChannelGroup.printGroupState(); - internalServer.start(); - } - - public void init() throws Exception { - new PropertiesLoader().load(System.getProperty("user.dir")); - cache = PropertiesCache.getInstance(); - addShutdownHook(); - } - - public void start() throws Exception { - bossGroup = new NioEventLoopGroup(FrameConstant.BOSSGROUP_NUM); - workerGroup = new NioEventLoopGroup(); - ServerBootstrap b = new ServerBootstrap(); - ChannelInitializer channelInit = new ChannelInitializer() { - @Override - protected void initChannel(SocketChannel ch) throws Exception { - ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(FrameConstant.FRAME_MAX_BYTES, - FrameConstant.FRAME_LEN_INDEX, FrameConstant.FRAME_LEN_LEN)) - .addLast(new ByteToPojoDecoder()) - .addLast(new PojoToByteEncoder()) - .addLast(new IdleStateHandler(0, 0, HEARTBEAT_TIMEOUT, TimeUnit.MINUTES)) - .addLast(new CustomEventHandler()) - .addLast(new InternalServerHandler()); - } - }; - b.group(bossGroup, workerGroup) - .channel(NioServerSocketChannel.class) - .option(ChannelOption.SO_BACKLOG, FrameConstant.TCP_SO_BACKLOG) - .childHandler(channelInit) - .childOption(ChannelOption.TCP_NODELAY, Boolean.TRUE); - - f = b.bind(cache.getInt(PORT)).sync(); - log.debug("InternalServer started on port {}......", cache.getInt(PORT)); - f.channel().closeFuture().sync(); - } -} diff --git a/nat-server/src/main/java/server/ProxyServer.java b/nat-server/src/main/java/server/ProxyServer.java deleted file mode 100644 index 30f7134..0000000 --- a/nat-server/src/main/java/server/ProxyServer.java +++ /dev/null @@ -1,69 +0,0 @@ -package server; - -import core.cache.PropertiesCache; -import core.constant.FrameConstant; -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelOption; -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.channel.socket.SocketChannel; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.handler.timeout.IdleStateHandler; -import lombok.extern.slf4j.Slf4j; -import server.group.ServerChannelGroup; -import server.handler.ConverterHandler; -import server.handler.HttpHandler; -import server.handler.ProxyServerHandler; - -import java.util.concurrent.TimeUnit; - -/** - * @Author wneck130@gmail.com - * @Function proxy服务端,用来接受来自外部的TCP请求 - */ -@Slf4j -public class ProxyServer extends BaseServer { - - /** - * 代理程序对外提供的服务端口 - */ - private static String PORT = "proxy.server.port"; - - public void init() { - cache = PropertiesCache.getInstance(); - addShutdownHook(); - } - - public synchronized void start() throws Exception{ - //如果已经启动了proxyServer,静默 - if (ServerChannelGroup.proxyServer != null) { - return; - } - bossGroup = new NioEventLoopGroup(FrameConstant.BOSSGROUP_NUM); - workerGroup = new NioEventLoopGroup(); - ServerBootstrap b = new ServerBootstrap(); - ChannelInitializer channelInit = new ChannelInitializer(){ - @Override - protected void initChannel(SocketChannel ch) throws Exception { - ch.pipeline().addLast("converter", new ConverterHandler()) -// .addLast("http", new HttpHandler()) - .addLast(new IdleStateHandler(0, 0, 5, TimeUnit.MINUTES)) - .addLast(new ProxyServerHandler()); - } - }; - b.group(bossGroup, workerGroup) - .channel(NioServerSocketChannel.class) - .option(ChannelOption.SO_BACKLOG, FrameConstant.TCP_SO_BACKLOG) - .childHandler(channelInit) - .childOption(ChannelOption.TCP_NODELAY, Boolean.TRUE); - - f = b.bind(cache.getInt(PORT)).sync().addListener((future -> { - if (future.isSuccess()) { - ServerChannelGroup.proxyServer = f; - } - })); - log.debug("ProxyServer started on port " + cache.getInt(PORT) + "......"); - f.channel().closeFuture().sync(); - ServerChannelGroup.proxyServer = null; - } -} diff --git a/nat-server/src/main/java/server/decoder/ByteToPojoDecoder.java b/nat-server/src/main/java/server/decoder/ByteToPojoDecoder.java deleted file mode 100644 index c2ba7f1..0000000 --- a/nat-server/src/main/java/server/decoder/ByteToPojoDecoder.java +++ /dev/null @@ -1,71 +0,0 @@ -package server.decoder; - -import core.constant.FrameConstant; -import core.entity.Frame; -import core.utils.BufUtil; -import core.utils.ByteUtil; -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.ByteToMessageDecoder; -import lombok.extern.slf4j.Slf4j; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * @Author wneck130@gmail.com - * @function 解码器,字节转Java对象 - */ -@Slf4j -public class ByteToPojoDecoder extends ByteToMessageDecoder { - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { - if (in.readableBytes() < FrameConstant.FRAME_MIN_LEN) { - log.error("invalidate msg : " + ByteUtil.toHexString(BufUtil.getArray(in))); - ctx.close(); - return; - } - byte pv = in.readByte(); - long serial = in.readLong(); - byte cmd = in.readByte(); - int len = in.readInt(); - if (in.readableBytes() != len) { - log.error("invalidate msg : " + ByteUtil.toHexString(BufUtil.getArray(in))); - ctx.close(); - return; - } - Frame frame = new Frame(); - frame.setPv(pv); - frame.setSerial(serial); - frame.setCmd(cmd); - frame.setLen(len); - try { - switch (cmd) { - case 0x01: - break; - case 0x02: - break; - case 0x03: - break; - case 0x04: - break; - case (byte) 0xFF: - byte[] dataBytes = new byte[len]; - in.readBytes(dataBytes); - Map data = new HashMap<>(); - data.put("data", dataBytes); - frame.setData(data); - break; - default: - break; - } - } catch (Exception e) { - log.error("invalidate msg : " + ByteUtil.toHexString(BufUtil.getArray(in))); - ctx.close(); - return; - } - out.add(frame); - } -} diff --git a/nat-server/src/main/java/server/decoder/PojoToByteEncoder.java b/nat-server/src/main/java/server/decoder/PojoToByteEncoder.java deleted file mode 100644 index 1701ddb..0000000 --- a/nat-server/src/main/java/server/decoder/PojoToByteEncoder.java +++ /dev/null @@ -1,28 +0,0 @@ -package server.decoder; - -import core.entity.Frame; -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToByteEncoder; -import lombok.extern.slf4j.Slf4j; - -/** - * @author wneck130@gmail.com - * @function POJO对象编码器,将POJO对象转化成字节流 - */ -@Slf4j -public class PojoToByteEncoder extends MessageToByteEncoder { - - @Override - protected void encode(ChannelHandlerContext ctx, Frame msg, ByteBuf out) throws Exception { - out.writeByte(msg.getPv()); - out.writeLong(msg.getSerial()); - out.writeByte(msg.getCmd()); - if (msg.getData() == null || msg.getData().get("data") == null) { - out.writeInt(0); - } else { - out.writeInt(((byte[]) msg.getData().get("data")).length); - out.writeBytes(((byte[]) msg.getData().get("data"))); - } - } -} diff --git a/nat-server/src/main/java/server/group/ServerChannelGroup.java b/nat-server/src/main/java/server/group/ServerChannelGroup.java deleted file mode 100644 index f015279..0000000 --- a/nat-server/src/main/java/server/group/ServerChannelGroup.java +++ /dev/null @@ -1,330 +0,0 @@ -package server.group; - -import core.cache.PropertiesCache; -import core.constant.FrameConstant; -import core.utils.BufUtil; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelId; -import io.netty.channel.group.ChannelGroup; -import io.netty.channel.group.DefaultChannelGroup; -import io.netty.util.concurrent.GlobalEventExecutor; -import lombok.extern.slf4j.Slf4j; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ScheduledFuture; -import java.util.stream.Collectors; - -/** - * @Author wneck130@gmail.com - * @Function 服务端channel组管理类 - */ -@Slf4j -public class ServerChannelGroup { - - private static PropertiesCache cache = PropertiesCache.getInstance(); - - /** - * 代理程序内部连接的默认连接数 - */ - private static String INIT_NUM = "internal.channel.init.num"; - - /** - * 代理程序内部连接短缺时扩展的连接数 - */ - private static String EXPAND_NUM = "channel.pool.expand.num"; - - /** - * 代理程序内部连接最大空闲连接数,超过时触发回收 - */ - private static String MAX_IDLE = "internal.channel.max.idle.num"; - - /** - * 缓存的proxyServer对象,用来判断当前proxyServer状态 - */ - public static ChannelFuture proxyServer; - - /** - * 系统连接的缓存 - */ - private static Map sysChannel = new ConcurrentHashMap<>(); - - /** - * channel对,将ProxyServer与InternalServer的channel进行配对,便于消息转发 - * key为InternalServer中channel的channelId,value为ProxyServer中channel的channelId - */ - private static Map channelPair = new ConcurrentHashMap<>(); - - - /** - * proxyChannel每次发送数据都会期待在timeout时间内收到响应,如果超时则认为连接异常,断开proxyChannel重新服务 - * ScheduledFuture为当前channel对应eventloop中的一个定义关闭任务,超时后关闭连接 - */ - private static Map futureMap = new ConcurrentHashMap<>(); - - /** - * 被代理服务的channel组 - */ - private static ChannelGroup proxyGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); - - /** - * 内部服务的channel组 - */ - private static ChannelGroup internalGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); - - /** - * 内部服务的channel组 - */ - private static ChannelGroup idleInternalGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); - - public static Map getChannelPair() { - return channelPair; - } - - public static void setChannelPair(Map channelPair) { - ServerChannelGroup.channelPair = channelPair; - } - - public static void setInternalGroup(ChannelGroup internalGroup) { - ServerChannelGroup.internalGroup = internalGroup; - } - - public static ChannelGroup getIdleInternalGroup() { - return idleInternalGroup; - } - - public static void setIdleInternalGroup(ChannelGroup idleInternalGroup) { - ServerChannelGroup.idleInternalGroup = idleInternalGroup; - } - - public static Map getSysChannel() { - return sysChannel; - } - - public static Map getchannelPair() { - return channelPair; - } - - public static ChannelGroup getProxyGroup() { - return proxyGroup; - } - - public static ChannelGroup getInternalGroup() { - return internalGroup; - } - - - public static void addProxyChannel(Channel channel) { - proxyGroup.add(channel); - } - - public static void removeProxyChannel(Channel channel) throws Exception{ - proxyGroup.remove(channel); - } - - public static void addInternalChannel(Channel channel) { - internalGroup.add(channel); - } - - public static void removeInternalChannel(Channel channel) throws Exception{ - internalGroup.remove(channel); - } - - public static void removeIdleInternalChannel(Channel channel) throws Exception{ - idleInternalGroup.remove(channel); - } - - - public static void addIdleInternalChannel(Channel channel) { - idleInternalGroup.add(channel); - } - - public static boolean idleInternalGroupIsEmpty() { - if (idleInternalGroup == null) { - return Boolean.TRUE; - } - return idleInternalGroup.size() < 1; - } - - /** - * 释放占用的连接池连接 - * @param channel - */ - public static void releaseInternalChannel(Channel channel){ - internalGroup.remove(channel); - idleInternalGroup.add(channel); - } - - /** - * 从空闲连接池中取出一条连接并与当前proxy连接配对 - * @param proxyChannel - * @throws Exception - */ - public synchronized static Channel forkChannel(Channel proxyChannel) throws Exception{ - Channel idleChannel = idleInternalGroup.iterator().next(); - idleInternalGroup.remove(idleChannel); - internalGroup.add(idleChannel); - channelPair.put(idleChannel.id(), proxyChannel.id()); - proxyGroup.add(proxyChannel); - return idleChannel; - } - - - /** - * 按照配置重新扩容/缩减internalGroup的数量 - */ - public static void reSizeInternalChannelNum() { - //判断连接池剩余连接 - if(idleInternalGroup.size() < cache.getInt(INIT_NUM)){ - //发送连接池扩容命令 - ByteBuf byteBuf = Unpooled.buffer(); - byteBuf.writeByte(FrameConstant.pv); - long serial = System.currentTimeMillis(); - byteBuf.writeLong(serial); - byteBuf.writeShort(FrameConstant.CHANNEL_POOL_NUM_LEN + FrameConstant.VC_CODE_LEN); - //扩容连接池数量 - byteBuf.writeByte(cache.getInt(EXPAND_NUM)); - //计算校验和 - int vc = 0; - for (byte byteVal : BufUtil.getArray(byteBuf)) { - vc = vc + (byteVal & 0xFF); - } - byteBuf.writeByte(vc); - Channel sysClient = sysChannel.get("Sys"); - sysClient.writeAndFlush(byteBuf); - } - if(idleInternalGroup.size() > cache.getInt(MAX_IDLE)){ - //移除连接数量 - int num = idleInternalGroup.size() - cache.getInt(MAX_IDLE); - //发送移除部分连接命令 - ByteBuf byteBuf = Unpooled.buffer(); - byteBuf.writeByte(FrameConstant.pv); - long serial = System.currentTimeMillis(); - byteBuf.writeLong(serial); - byteBuf.writeShort(FrameConstant.CHANNEL_POOL_NUM_LEN + FrameConstant.VC_CODE_LEN); - //连接池移除数量 - byteBuf.writeByte(num); - //计算校验和 - int vc = 0; - for (byte byteVal : BufUtil.getArray(byteBuf)) { - vc = vc + (byteVal & 0xFF); - } - byteBuf.writeByte(vc); - Channel sysClient = sysChannel.get("Sys"); - sysClient.writeAndFlush(byteBuf); - } - } - - public static void addChannelPair(ChannelId internalChannelId, ChannelId proxyChannelId) { - channelPair.put(internalChannelId, proxyChannelId); - } - - public static void removeChannelPair(ChannelId internalChannelId, ChannelId proxyChannelId) throws Exception{ - channelPair.remove(internalChannelId, proxyChannelId); - } - - public static void removeChannelPair(ChannelId channelId) throws Exception{ - channelPair.remove(channelId); - List result = channelPair.entrySet().stream() - .filter(x -> Objects.equals(x.getValue(), channelId)) - .map(y -> y.getKey()) - .collect(Collectors.toList()); - if (result.isEmpty() || result.size() != 1) { - throw new Exception("channel移除异常!!!"); - } - } - - /** - * 判断当前channel是否已经建立channelPair,没有建立channelPair的channel无法进行转发 - * @param channelId - * @return - */ - public static boolean channelPairExist(ChannelId channelId) { - if (channelPair.isEmpty()) { - return Boolean.FALSE; - } - if (channelPair.containsKey(channelId)) { - return Boolean.TRUE; - } - if (channelPair.containsValue(channelId)) { - return Boolean.TRUE; - } - return Boolean.FALSE; - } - - - - /** - * 根据内部channleId获取代理channel - * @param channelId - * @return - */ - public static Channel getProxyByInternal(ChannelId channelId) throws Exception{ - ChannelId proxyChannelId = channelPair.get(channelId); - return proxyGroup.find(proxyChannelId); - } - - - - /** - * 根据代理channleId获取内部channel - * @param channelId - * @return - */ - public static Channel getInternalByProxy(ChannelId channelId) throws Exception{ - List result = channelPair.entrySet().stream() - .filter(e -> Objects.equals(e.getValue(), channelId)) - .map((x) -> x.getKey()) - .collect(Collectors.toList()); - if (result.isEmpty() || result.size() != 1) { - log.error("channel匹配异常!!!"); - return null; - } - return internalGroup.find(result.get(0)); - } - - /** - * 打印当前channelGroup情况 - */ - public static void printGroupState() { - log.info("当前channel情况:\r\n" - + "IdleInternalChannel数量:" + ServerChannelGroup.getIdleInternalGroup().size() + "\r\n" - + "InternalChannel数量:" + ServerChannelGroup.getInternalGroup().size() + "\r\n" - + "当前channelPair:\r\n"); - ServerChannelGroup.getchannelPair() - .forEach((key, value) -> log.debug("[InternalChannel:" + key + ", ProxyChannel:" + value + "]\r\n")); - } - - /** - * 存储超时关闭的定时任务,如果已经存在任务,则cancel新任务,保持原任务不变 - * @param channelId - * @param future - */ - public static void addFuture(ChannelId channelId, ScheduledFuture future) { - try { - ScheduledFuture successOne = futureMap.putIfAbsent(channelId, future); - if (!Objects.equals(successOne, future)) { - future.cancel(true); - } - } catch (Exception e) { - e.printStackTrace(); - log.error("超时关闭proxyChannel异常!!!", e); - } - } - - public static void cancelFuture(ChannelId channelId) { - try { - futureMap.get(channelId).cancel(true); - } catch (Exception e) { - e.printStackTrace(); - log.error("关闭ScheduledFuture任务异常!!!", e); - } - } -} diff --git a/nat-server/src/main/java/server/handler/ConverterHandler.java b/nat-server/src/main/java/server/handler/ConverterHandler.java deleted file mode 100644 index 21da8c8..0000000 --- a/nat-server/src/main/java/server/handler/ConverterHandler.java +++ /dev/null @@ -1,55 +0,0 @@ -package server.handler; - -import core.constant.FrameConstant; -import core.utils.BufUtil; -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.handler.codec.http.HttpMethod; - -/** - * @author wneck130@gmail.com - * @function handler转化器,根据首次请求的内容重新设置pipeline中的handler - */ -public class ConverterHandler extends SimpleChannelInboundHandler { - - - /** - * 通过报文内容判断是否为http/https请求 - * 如果是https的connect请求,则直接返回固定报文给客户端,后续所有加密通信内容不做干涉 - * 如果是http/https请求,将请求头中的host信息去除 - * 如果是tcp自定义报文,则将本handler和httpHandler移除 - * HTTP请求由请求行、请求头、请求体构成,每个部分由换行符分隔,同一部分内部使用空格分隔,请求行内容为请求方法,URI,协议版本 - * - * @param ctx - * @param msg - * @throws Exception - */ - @Override - protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { - try { - String msgStr = new String(BufUtil.getArray(msg), FrameConstant.DEFAULT_CHARSET); - String headeLine = msgStr.split("\\n")[0]; - //判断是否为https的connect请求,处理浏览器的代理连接请求 - if (headeLine.substring(0, headeLine.indexOf(" ") + 1).equalsIgnoreCase(HttpMethod.CONNECT.name())) { - ByteBuf buffer = ctx.channel().alloc().buffer("HTTP/1.1 200 Connection Established\r\n\r\n".getBytes().length); - buffer.writeBytes("HTTP/1.1 200 Connection Established\r\n\r\n".getBytes()); - ctx.channel().writeAndFlush(buffer); - return; - } else { - //普通http请求直接调整handler,转发给下一个handler - ctx.pipeline().remove("converter"); - msg.retain(); - ctx.fireChannelRead(msg); - return; - } - } catch (Exception e) { - e.printStackTrace(); - } - //如果无法处理为http请求,则移除对应http相关handler,并将消息转发至下一个handler - ctx.pipeline().remove("converter"); - ctx.pipeline().remove("http"); - msg.retain(); - ctx.fireChannelRead(msg); - } -} diff --git a/nat-server/src/main/java/server/handler/HttpHandler.java b/nat-server/src/main/java/server/handler/HttpHandler.java deleted file mode 100644 index 8e22a6f..0000000 --- a/nat-server/src/main/java/server/handler/HttpHandler.java +++ /dev/null @@ -1,46 +0,0 @@ -package server.handler; - -import core.constant.FrameConstant; -import core.utils.BufUtil; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelOutboundHandlerAdapter; -import io.netty.channel.ChannelPromise; - -/** - * @author wneck130@gmail.com - * @function 简易的http报文内容替换,将http请求头中的host信息去除 - */ -public class HttpHandler extends ChannelOutboundHandlerAdapter { - - private static final String HTTP_HOST = "Host"; - - private static final String HTTP_CONTENT_LENGTH = "Content-Length"; - - private static final String HTTP_METHOD = "GET"; - - /** - * 如果采用http1.0协议发送get请求,请求头中的Content-Length值与实际http包大小不一致, - * 会导致异常,Content-Length值偏小则超时,Content-Length值偏大则包内容补全 - * - * @param ctx - * @param msg - * @throws Exception - */ - @Override - public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { - String msgStr = new String(BufUtil.getArray((ByteBuf) msg), FrameConstant.DEFAULT_CHARSET); - if (msgStr.contains(HTTP_CONTENT_LENGTH)) { - String httpMsg = msgStr.substring(0, - msgStr.indexOf(HTTP_CONTENT_LENGTH)) - + msgStr.substring(msgStr.indexOf(HTTP_CONTENT_LENGTH)) - .substring(msgStr.substring(msgStr.indexOf(HTTP_CONTENT_LENGTH)).indexOf("\r\n") - + 2); - ByteBuf newMsg = Unpooled.buffer().writeBytes(httpMsg.getBytes(FrameConstant.DEFAULT_CHARSET)); - ctx.writeAndFlush(newMsg); - } - ctx.writeAndFlush(msg); - } - -} diff --git a/nat-server/src/main/java/server/handler/InternalServerHandler.java b/nat-server/src/main/java/server/handler/InternalServerHandler.java deleted file mode 100644 index 54923fe..0000000 --- a/nat-server/src/main/java/server/handler/InternalServerHandler.java +++ /dev/null @@ -1,99 +0,0 @@ -package server.handler; - -import core.entity.Frame; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import lombok.extern.slf4j.Slf4j; -import server.ProxyServer; -import server.group.ServerChannelGroup; -import server.handler.processor.*; - -/** - * @Author wneck130@gmail.com - * @function 业务处理handler,所有协议命令在本类中处理 - */ -@Slf4j -public class InternalServerHandler extends SimpleChannelInboundHandler { - /** - * 启动代理服务端暂时使用锁保证一次 - * - * @param ctx - */ - static synchronized void startProxyServer(ChannelHandlerContext ctx) { - if (ServerChannelGroup.idleInternalGroupIsEmpty()) { - new Thread(() -> { - try { - ProxyServer proxyServer = new ProxyServer(); - proxyServer.init(); - proxyServer.start(); - } catch (Exception e) { - e.printStackTrace(); - log.error("启动ProxyServer异常:" + e); - } - }).start(); - ServerChannelGroup.addIdleInternalChannel(ctx.channel()); - } else { - ServerChannelGroup.addIdleInternalChannel(ctx.channel()); - } - } - - @Override - protected void channelRead0(ChannelHandlerContext ctx, Frame msg) throws Exception { - byte cmd = msg.getCmd(); - switch (cmd) { - //接入命令 - case 0x01: - new LoginProcessor().process(ctx, msg); - break; - //心跳命令 - case 0x02: - new HeartbeatProcessor().process(ctx, msg); - break; - //连接池扩容 - case 0x03: - new ConnectionExpandProcessor().process(ctx, msg); - break; - //连接池回收 - case 0x04: - new ConnectionReduceProcessor().process(ctx, msg); - break; - //消息转发 - case (byte) 0xff: - new DataTransferProcessor().process(ctx, msg); - break; - default: - throw new IllegalStateException("Unexpected value: " + cmd); - } - } - - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - //当第一条可用的internalChannel建立时,启动proxyServer开始提供代理服务 - if (ServerChannelGroup.idleInternalGroupIsEmpty()) { - startProxyServer(ctx); - } else { - ServerChannelGroup.addIdleInternalChannel(ctx.channel()); - } - log.debug("建立连接:ServerInternal--[{}]--ClientInternal", ctx.channel().id()); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - //普通internalChannel断开连接,则回收idleInternalGroup和internalGroup中的连接,等待客户端重新发起连接补足连接数 - ServerChannelGroup.removeIdleInternalChannel(ctx.channel()); - ServerChannelGroup.removeInternalChannel(ctx.channel()); - } - - /** - * 通道异常触发 - * - * @param ctx - * @param cause - * @throws Exception - */ - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - log.error("ServerInternal[{}]发生异常:{}", ctx.channel().id(), cause.getStackTrace()); - ctx.close(); - } -} diff --git a/nat-server/src/main/java/server/handler/ProxyServerHandler.java b/nat-server/src/main/java/server/handler/ProxyServerHandler.java deleted file mode 100644 index abc5fc3..0000000 --- a/nat-server/src/main/java/server/handler/ProxyServerHandler.java +++ /dev/null @@ -1,169 +0,0 @@ -package server.handler; - -import core.entity.Frame; -import core.enums.CommandEnum; -import core.utils.BufUtil; -import core.utils.ByteUtil; -import io.netty.buffer.ByteBuf; -import io.netty.channel.Channel; -import io.netty.channel.ChannelFutureListener; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import lombok.extern.slf4j.Slf4j; -import server.group.ServerChannelGroup; - -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; - -/** - * @Author wneck130@gmail.com - * @Function proxyServer业务handler - */ -@Slf4j -public class ProxyServerHandler extends SimpleChannelInboundHandler { - - /** - * 数据传输时触发 - * @param ctx - * @param msg - * @throws Exception - */ - @Override - protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { - - if (ServerChannelGroup.channelPairExist(ctx.channel().id())) { - //已经存在配对,直接进行消息转发 - Channel internalChannel = ServerChannelGroup.getInternalByProxy(ctx.channel().id()); - byte[] print = new byte[5]; - msg.getBytes(0, print); - log.debug("\nRequestor数据:"+ ByteUtil.toHexString(BufUtil.getArray(msg)) - +"\r\nRequestor>>[{}]>>ServerProxy----ServerInternal--[{}]--Client", - ctx.channel().id(), internalChannel.id()); - if(internalChannel != null) { - byte[] message = new byte[msg.readableBytes()]; - msg.readBytes(message); - Map map = new HashMap<>(); - map.put("data", message); - Frame frame = new Frame(); - frame.setCmd(CommandEnum.CMD_DATA_TRANSFER.getCmd()); - frame.setData(map); - internalChannel.writeAndFlush(frame).addListener((ChannelFutureListener) future -> { - if (!future.isSuccess()) { - log.error("发送数据异常: ", future.cause() - + "\nRequestor--[{}]--ServerProxy--XX--ServerInternal--[{}]--Client", - ctx.channel().id(), internalChannel.id()); - //断开代理服务连接,重新建立channel配对并对外提供服务 - ctx.close(); - } else { - ScheduledFuture scheduledFuture = ctx.channel().eventLoop() - .schedule(() -> { - log.error("ProxyChannel["+ctx.channel().id()+"]超时未响应,关闭连接!!!"); - ctx.close(); - }, 5, TimeUnit.SECONDS); - ServerChannelGroup.addFuture(ctx.channel().id(), scheduledFuture); - } - }); - }else { - log.error("配对的internalChannel已失效!!!" - + "\r\nRequestor--[{}]--ServerProxy----ServerInternal--[XX]--Client", - ctx.channel().id()); - //断开代理服务连接,重新建立channel配对并对外提供服务 - ctx.channel().close(); - } - } else { - log.error("找不到配对的internalChannel!!!" - + "\r\nRequestor--[{}]--ServerProxy----ServerInternal--[XX]--Client", - ctx.channel().id()); - //断开代理服务连接,重新建立channel配对并对外提供服务 - ctx.channel().close(); - } - } - - /** - * 通道连接成功触发 - * @param ctx - * @throws Exception - */ - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - //新的连接建立后进行配对 - Channel internalChannel = ServerChannelGroup.forkChannel(ctx.channel()); - fullyConnect(internalChannel); - ServerChannelGroup.printGroupState(); - log.debug("建立连接:Requestor--[{}]--ServerProxy----ServerInternal--[{}]--Client", - ctx.channel().id(), internalChannel.id()); - } - - /** - * 通道断开时触发 - * @param ctx - * @throws Exception - */ - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - Channel proxyChannel = ctx.channel(); - log.debug("proxyChannel:"+ proxyChannel.id() + " 断开连接!!!"); - if (ServerChannelGroup.channelPairExist(proxyChannel.id())) { - ServerChannelGroup.printGroupState(); - Channel internalChannel = ServerChannelGroup.getInternalByProxy(proxyChannel.id()); - ServerChannelGroup.removeProxyChannel(ctx.channel()); - if (internalChannel == null) { - log.error("与proxyChannel:"+proxyChannel.id()+"配对的internalChannel为null"); - return; - } - ServerChannelGroup.removeChannelPair(internalChannel.id(), ctx.channel().id()); - ServerChannelGroup.releaseInternalChannel(internalChannel); - //确保客户端连接情况和服务端一致,发送连接回收命令给客户端 - Frame frame = new Frame(); - frame.setCmd(CommandEnum.CMD_CHANNEL_RECYCLE.getCmd()); - internalChannel.writeAndFlush(frame).addListener((ChannelFutureListener) future -> { - if (!future.isSuccess()) { - log.error("发送连接回收命令异常: ", future.cause() - + "\nRequestor--[{}]--ServerProxy--XX--ServerInternal--[{}]--Client", - ctx.channel().id(), internalChannel.id()); - } - }); - } else { - log.error("proxyChannel:"+ proxyChannel.id() + " 未找到配对关系!!!"); - ServerChannelGroup.printGroupState(); - } - } - - /** - * 通道异常触发 - * @param ctx - * @param cause - * @throws Exception - */ - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - Channel channel = ctx.channel(); - if(!channel.isActive()){ - ctx.close(); - } - log.error("ServerProxy[{}]发生异常:{}", ctx.channel().id(), cause.getStackTrace()); - } - - /** - * proxyChannel与internalChannel建立配对后,通知客户端建立与被代理服务的连接并缓存配对关系 - */ - public void fullyConnect(Channel internalChannel) throws Exception{ - //发送启动代理客户端命令 - Frame frame = new Frame(); - frame.setCmd(CommandEnum.CMD_START_PROXY_CLIENT.getCmd()); - Channel proxyChannel = ServerChannelGroup.getProxyByInternal(internalChannel.id()); - internalChannel.writeAndFlush(frame).addListener((ChannelFutureListener) future -> { - if (!future.isSuccess()) { - log.debug("Server数据发送异常:"+ frame.toString() - +"\r\nRequestor--[{}]--ServerProxy--XX--ServerInternal--[{}]--Client", - proxyChannel.id(), internalChannel.id()); - } else { - log.debug("Server数据:"+ frame.toString() - +"\r\nRequestor--[{}]--ServerProxy>>>>ServerInternal--[{}]--Client", - proxyChannel.id(), internalChannel.id()); - } - }); - } -} diff --git a/nat-server/src/main/java/server/handler/processor/ConnectionExpandProcessor.java b/nat-server/src/main/java/server/handler/processor/ConnectionExpandProcessor.java deleted file mode 100644 index 679c652..0000000 --- a/nat-server/src/main/java/server/handler/processor/ConnectionExpandProcessor.java +++ /dev/null @@ -1,17 +0,0 @@ -package server.handler.processor; - -import core.entity.Frame; -import core.processor.Processor; -import io.netty.channel.ChannelHandlerContext; - -/** - * @Author wneck130@gmail.com - * @Function - */ -public class ConnectionExpandProcessor implements Processor { - - @Override - public void process(ChannelHandlerContext ctx, Frame msg) throws Exception { - //客户端对建立连接池命令的响应,无业务需要暂时不实现 - } -} diff --git a/nat-server/src/main/java/server/handler/processor/ConnectionReduceProcessor.java b/nat-server/src/main/java/server/handler/processor/ConnectionReduceProcessor.java deleted file mode 100644 index f59620d..0000000 --- a/nat-server/src/main/java/server/handler/processor/ConnectionReduceProcessor.java +++ /dev/null @@ -1,18 +0,0 @@ -package server.handler.processor; - -import core.entity.Frame; -import core.processor.Processor; -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; - -/** - * @Author wneck130@gmail.com - * @Function - */ -public class ConnectionReduceProcessor implements Processor { - - @Override - public void process(ChannelHandlerContext ctx, Frame msg) throws Exception { - - } -} diff --git a/nat-server/src/main/java/server/handler/processor/DataTransferProcessor.java b/nat-server/src/main/java/server/handler/processor/DataTransferProcessor.java deleted file mode 100644 index 18cab26..0000000 --- a/nat-server/src/main/java/server/handler/processor/DataTransferProcessor.java +++ /dev/null @@ -1,56 +0,0 @@ -package server.handler.processor; - -import core.constant.FrameConstant; -import core.entity.Frame; -import core.processor.Processor; -import core.utils.ByteUtil; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandlerContext; -import lombok.extern.slf4j.Slf4j; -import server.group.ServerChannelGroup; - -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.util.Arrays; - -/** - * @Author wneck130@gmail.com - * @function 数据交互处理器,转发来自internalClient的业务消息 - */ -@Slf4j -public class DataTransferProcessor implements Processor { - - @Override - public void process(ChannelHandlerContext ctx, Frame msg) throws Exception { - Channel internalChannel = ctx.channel(); - if (ServerChannelGroup.channelPairExist(internalChannel.id())) { - //根据internalChannel找到对应的proxyChannel - Channel proxyChannel = ServerChannelGroup.getProxyByInternal(internalChannel.id()); - if (proxyChannel == null) { - log.error("配对关系异常:[InternalChannel:{}, ProxyChannel:null]", internalChannel); - throw new Exception("配对关系异常:[InternalChannel:"+internalChannel+", ProxyChannel:null]"); - } - ServerChannelGroup.cancelFuture(proxyChannel.id()); - log.debug("Responsor数据:" + msg.toString() + - "\n Requestor--[{}]--ServerProxy----ServerInternal<<[{}]< { - if (future.isSuccess()) { - log.debug("Responsor数据:" + msg.toString() + - "\n Requestor<<[{}]< { + //启动成功则为每个命令注册响应监听器 + if (future.isSuccess()) { + MessageContext messageContext = MessageContext.getInstance(); + //将所有指令的处理器都指派给messageContext作为监听器 + Arrays.stream(ProcessorEnum.values()).forEach(processorEnum -> { + messageContext.addResponseListener(ProcessorManager.getInstance(processorEnum.getClazz())); + }); + } else { + //启动不成功在一定的延迟之后进行重启 + nioServerFuture.channel().eventLoop() + .schedule(() -> this.start(port), 10, TimeUnit.SECONDS); + } + }; + addShutdownHook(); + } + + @Override + public void start(int port) { + init(); + this.port = port; + //如果已经启动了proxyServer,静默 + if (nioServerFuture != null && nioServerFuture.channel().isOpen()) { + return; + } + try { + bossGroup = new NioEventLoopGroup(FrameConstant.BOSSGROUP_NUM); + workerGroup = new NioEventLoopGroup(); + ServerBootstrap b = new ServerBootstrap(); + ChannelInitializer channelInit = new ChannelInitializer() { + @Override + protected void initChannel(SocketChannel ch) throws Exception { + ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(FrameConstant.FRAME_MAX_BYTES, + FrameConstant.FRAME_LEN_INDEX, FrameConstant.FRAME_LEN_LEN)) + .addLast(new IdleStateHandler(0, 0, HEARTBEAT_TIMEOUT, TimeUnit.MINUTES)) + .addLast(new ByteToPojoDecoder()) + .addLast(new PojoToByteEncoder()) + .addLast(new MessageReceiveFilter()) + .addLast(new MessageSendFilter()) + .addLast(new CustomEventHandler()) + .addLast(new InternalServerHandler()); + } + }; + b.group(bossGroup, workerGroup) + .channel(NioServerSocketChannel.class) + .option(ChannelOption.SO_BACKLOG, FrameConstant.TCP_SO_BACKLOG) + .option(ChannelOption.SO_REUSEADDR, FrameConstant.TCP_REUSE_ADDR) + .childOption(ChannelOption.TCP_NODELAY, FrameConstant.TCP_NODELAY) + .childHandler(channelInit) + .childOption(ChannelOption.TCP_NODELAY, Boolean.TRUE); + + nioServerFuture = b.bind(cache.getInt(PORT)).sync(); + log.debug("InternalServer started on port {}......", cache.getInt(PORT)); + //添加关闭重启的监听器,3秒后尝试重启 + nioServerFuture.addListener(genericFutureListener); + nioServerFuture.channel().closeFuture().sync(); + } catch (Exception e) { + log.error("InternalServer started failed while listening on port {}!!!", cache.getInt(PORT), e); + } + } + + @Override + public boolean isHeathy() { + return nioServerFuture.channel().isOpen(); + } + + @Override + public boolean isRunning() { + return nioServerFuture.channel().isActive(); + } + + @Override + public void close() { + nioServerFuture.removeListener(genericFutureListener); + nioServerFuture.channel().close(); + log.info("ProxyServer closed success"); + } +} diff --git a/nat-server/src/main/java/server/internal/decoder/ByteToPojoDecoder.java b/nat-server/src/main/java/server/internal/decoder/ByteToPojoDecoder.java new file mode 100644 index 0000000..a7d6174 --- /dev/null +++ b/nat-server/src/main/java/server/internal/decoder/ByteToPojoDecoder.java @@ -0,0 +1,35 @@ +package server.internal.decoder; + +import core.constant.FrameConstant; +import core.entity.Frame; +import core.netty.handler.processor.ProcessorManager; +import core.utils.BufUtil; +import core.utils.ByteUtil; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ByteToMessageDecoder; +import lombok.extern.slf4j.Slf4j; +import server.internal.handler.processor.constant.ProcessorEnum; + +import java.util.List; + +/** + * @Author wneck130@gmail.com + * @function 解码器,字节转Java对象 + */ +@Slf4j +public class ByteToPojoDecoder extends ByteToMessageDecoder { + + @Override + protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { + if (in.readableBytes() < FrameConstant.FRAME_MIN_LEN) { + log.error("无法解析的消息 : " + ByteUtil.toHexString(BufUtil.getArray(in))); + ctx.close(); + return; + } + log.debug("收到消息:{}", ByteUtil.toHexString(BufUtil.getArray(in))); + byte cmd = in.getByte(FrameConstant.FRAME_CMD_INDEX); + Frame frame = ProcessorManager.getInstance(ProcessorEnum.getClassByCmd(cmd)).assemble(in); + out.add(frame); + } +} diff --git a/nat-server/src/main/java/server/internal/decoder/PojoToByteEncoder.java b/nat-server/src/main/java/server/internal/decoder/PojoToByteEncoder.java new file mode 100644 index 0000000..4abc57f --- /dev/null +++ b/nat-server/src/main/java/server/internal/decoder/PojoToByteEncoder.java @@ -0,0 +1,52 @@ +package server.internal.decoder; + +import core.constant.FrameConstant; +import core.entity.Frame; +import core.utils.BufUtil; +import core.utils.ByteUtil; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToByteEncoder; +import lombok.extern.slf4j.Slf4j; + +import java.nio.charset.StandardCharsets; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * @author wneck130@gmail.com + * @function 编码器,将POJO对象转化成字节流 + */ +@Slf4j +public class PojoToByteEncoder extends MessageToByteEncoder { + + @Override + protected void encode(ChannelHandlerContext ctx, Frame msg, ByteBuf out) throws Exception { + out.writeByte(msg.getPv()); + out.writeLong(msg.getSerial()); + out.writeBytes(msg.getReq().getBytes(StandardCharsets.UTF_8)); + out.writeBytes(msg.getRes().getBytes(StandardCharsets.UTF_8)); + out.writeByte(msg.getCmd()); + if (msg.getData() == null) { + out.writeInt(0); + } else { + //填充默认值 + out.writeInt(0); + LinkedHashMap dataMap = msg.getData(); + int length = dataMap.entrySet().stream().map(Map.Entry::getValue).mapToInt(value -> { + if (value instanceof Byte) { + out.writeByte((byte) value); + return 1; + } else if (value instanceof byte[]) { + out.writeBytes((byte[]) value); + return ((byte[]) value).length; + } else { + //TODO 待实现多种数据类型的字节转化 + return 0; + } + }).sum(); + out.setBytes(FrameConstant.FRAME_LEN_INDEX, ByteUtil.fromInt(length)); + log.debug("发送消息:{}", ByteUtil.toHexString(BufUtil.getArray(out))); + } + } +} diff --git a/nat-server/src/main/java/server/handler/CustomEventHandler.java b/nat-server/src/main/java/server/internal/handler/CustomEventHandler.java similarity index 95% rename from nat-server/src/main/java/server/handler/CustomEventHandler.java rename to nat-server/src/main/java/server/internal/handler/CustomEventHandler.java index 77752c1..158a127 100644 --- a/nat-server/src/main/java/server/handler/CustomEventHandler.java +++ b/nat-server/src/main/java/server/internal/handler/CustomEventHandler.java @@ -1,4 +1,4 @@ -package server.handler; +package server.internal.handler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; diff --git a/nat-server/src/main/java/server/internal/handler/InternalServerHandler.java b/nat-server/src/main/java/server/internal/handler/InternalServerHandler.java new file mode 100644 index 0000000..d248263 --- /dev/null +++ b/nat-server/src/main/java/server/internal/handler/InternalServerHandler.java @@ -0,0 +1,49 @@ +package server.internal.handler; + +import core.entity.Frame; +import core.netty.group.ServerChannelGroup; +import core.netty.handler.processor.ProcessorManager; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import lombok.extern.slf4j.Slf4j; +import server.internal.handler.processor.constant.ProcessorEnum; + +/** + * @Author wneck130@gmail.com + * @function 业务处理handler,所有协议命令在本类中处理 + */ +@Slf4j +public class InternalServerHandler extends SimpleChannelInboundHandler { + + @Override + protected void channelRead0(ChannelHandlerContext ctx, Frame msg) throws Exception { + byte cmd = msg.getCmd(); + ProcessorManager.getInstance(ProcessorEnum.getClassByCmd(cmd)).request(ctx, msg); + } + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + //internal channel创建时,维护internalGroup + ServerChannelGroup.addInternal(ctx.channel()); + log.debug("channel[{}]进入internalChannel组!!!", ctx.channel().id().asShortText()); + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + //internal channel销毁时,维护internalGroup + ServerChannelGroup.removeInternal(ctx.channel()); + } + + /** + * 通道异常触发 + * + * @param ctx + * @param cause + * @throws Exception + */ + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + log.error("ServerInternal[{}]发生异常:{}", ctx.channel().id(), cause.getStackTrace(), cause); + ctx.close(); + } +} diff --git a/nat-server/src/main/java/server/internal/handler/processor/ConnectionExpandProcessor.java b/nat-server/src/main/java/server/internal/handler/processor/ConnectionExpandProcessor.java new file mode 100644 index 0000000..41dbd94 --- /dev/null +++ b/nat-server/src/main/java/server/internal/handler/processor/ConnectionExpandProcessor.java @@ -0,0 +1,18 @@ +package server.internal.handler.processor; + +/** + * @Author wneck130@gmail.com + * @Function + */ +//public class ConnectionExpandProcessor implements Processor { +// +// @Override +// public Frame assemble(ByteBuf in) throws Exception { +// return null; +// } +// +// @Override +// public void request(ChannelHandlerContext ctx, Frame msg) throws Exception { +// //客户端对建立连接池命令的响应,无业务需要暂时不实现 +// } +//} diff --git a/nat-server/src/main/java/server/internal/handler/processor/ConnectionReduceProcessor.java b/nat-server/src/main/java/server/internal/handler/processor/ConnectionReduceProcessor.java new file mode 100644 index 0000000..880bfa1 --- /dev/null +++ b/nat-server/src/main/java/server/internal/handler/processor/ConnectionReduceProcessor.java @@ -0,0 +1,18 @@ +package server.internal.handler.processor; + +/** + * @Author wneck130@gmail.com + * @Function + */ +//public class ConnectionReduceProcessor implements Processor { +// +// @Override +// public Frame assemble(ByteBuf in) throws Exception { +// return null; +// } +// +// @Override +// public void request(ChannelHandlerContext ctx, Frame msg) throws Exception { +// +// } +//} diff --git a/nat-server/src/main/java/server/internal/handler/processor/DownStreamProcessor.java b/nat-server/src/main/java/server/internal/handler/processor/DownStreamProcessor.java new file mode 100644 index 0000000..cabff3d --- /dev/null +++ b/nat-server/src/main/java/server/internal/handler/processor/DownStreamProcessor.java @@ -0,0 +1,86 @@ +package server.internal.handler.processor; + +import core.constant.FrameConstant; +import core.entity.Frame; +import core.netty.group.ServerChannelGroup; +import core.netty.group.channel.message.ResponseEvent; +import core.netty.handler.processor.Processor; +import core.utils.BufUtil; +import core.utils.ByteUtil; +import io.netty.buffer.ByteBuf; +import io.netty.channel.Channel; +import lombok.extern.slf4j.Slf4j; +import server.internal.handler.processor.constant.ProcessorEnum; + +import java.util.LinkedHashMap; +import java.util.Objects; +import java.util.UnknownFormatConversionException; + +/** + * @Author wneck130@gmail.com + * @function 数据下发处理器(cmd:0xEE),由proxyServer发起请求,proxyClient回复响应 + */ +@Slf4j +public class DownStreamProcessor implements Processor { + + /** + * downStream数据帧处理,在服务端,处理的是downStream命令的响应 + * @param in netty获取的TCP数据流 + * @return + * @throws Exception + */ + @Override + public Frame assemble(ByteBuf in) throws Exception { + try { + //解析协议公共部分 + Frame frame = new Frame().quickHead(in); + //解析协议data部分 + byte result = in.readByte(); + //生成数据帧 + LinkedHashMap dataMap = new LinkedHashMap<>(); + dataMap.put("result", result); + frame.setData(dataMap); + return frame; + } catch (Exception e) { + log.error("无法解析的消息: " + ByteUtil.toHexString(BufUtil.getArray(in))); + throw new UnknownFormatConversionException("无法解析的消息!!!"); + } + } + + @Override + public void response(ResponseEvent responseEvent) { + try { + byte result = (byte)responseEvent.getResponse().getData().get("result"); + if (result != FrameConstant.RESULT_SUCCESS) { + log.error("来自{}的消息收到客户端处理失败响应,尝试重发!!!", responseEvent.getChannelId()); + Channel internalChannel = ServerChannelGroup.forkChannel(responseEvent.getChannelId()); + internalChannel.writeAndFlush(responseEvent.getRequest()); + } + } catch (Exception e) { + log.error("channel[{}]处理响应指令CMD:{},SERIAL:{}重试失败", responseEvent.getChannelId(), + responseEvent.getRequest().getCmd(), responseEvent.getRequest().getSerial()); + } + } + + @Override + public void timeout(ResponseEvent responseEvent) { + try { + //超时后默认进行一次重试 + Channel internalChannel = ServerChannelGroup.forkChannel(responseEvent.getChannelId()); + internalChannel.writeAndFlush(responseEvent.getRequest()); + } catch (Exception e) { + log.error("channel[{}]处理超时指令CMD:{},SERIAL:{}重试失败", responseEvent.getChannelId(), + responseEvent.getRequest().getCmd(), responseEvent.getRequest().getSerial()); + } + } + + /** + * processor对应的命令字适配 + * @param cmd + * @return + */ + @Override + public boolean supply(byte cmd) { + return Objects.equals(ProcessorEnum.DOWN_STREAM.getCmd(), cmd); + } +} diff --git a/nat-server/src/main/java/server/internal/handler/processor/HeartbeatProcessor.java b/nat-server/src/main/java/server/internal/handler/processor/HeartbeatProcessor.java new file mode 100644 index 0000000..c890108 --- /dev/null +++ b/nat-server/src/main/java/server/internal/handler/processor/HeartbeatProcessor.java @@ -0,0 +1,67 @@ +package server.internal.handler.processor; + +import core.constant.FrameConstant; +import core.entity.Frame; +import core.netty.handler.processor.Processor; +import core.utils.BufUtil; +import core.utils.ByteUtil; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import lombok.extern.slf4j.Slf4j; +import server.internal.handler.processor.constant.ProcessorEnum; + +import java.util.Objects; +import java.util.UnknownFormatConversionException; + +/** + * @Author wneck130@gmail.com + * @Function 心跳命令处理器(cmd:0x02),命令由internalClient发起请求,internalServer回复响应 + */ +@Slf4j +public class HeartbeatProcessor implements Processor { + + /** + * heartbeat数据帧处理 + * @param in netty获取的TCP数据流 + * @return + * @throws Exception + */ + @Override + public Frame assemble(ByteBuf in) throws Exception { + try { + //解析协议公共部分 + return new Frame().quickHead(in); + } catch (Exception e) { + log.error("无法解析的消息: " + ByteUtil.toHexString(BufUtil.getArray(in))); + throw new UnknownFormatConversionException("无法解析的消息!!!"); + } + } + + /** + * 处理heartbeat业务 + * @param ctx netty channel上下文 + * @param msg 解析成Frame结构的TCP请求数据帧 + * @throws Exception + */ + @Override + public void request(ChannelHandlerContext ctx, Frame msg) throws Exception { + //保持连接活性,回复心跳 + Frame response = new Frame(); + response.setPv(FrameConstant.RES_PV); + response.setSerial(msg.getSerial()); + response.setReq(msg.getReq()); + response.setRes(ctx.channel().id().asShortText()); + response.setCmd(msg.getCmd()); + ctx.writeAndFlush(response); + } + + /** + * processor对应的命令字适配 + * @param cmd + * @return + */ + @Override + public boolean supply(byte cmd) { + return Objects.equals(ProcessorEnum.HEARTBEAT.getCmd(), cmd); + } +} diff --git a/nat-server/src/main/java/server/internal/handler/processor/LoginProcessor.java b/nat-server/src/main/java/server/internal/handler/processor/LoginProcessor.java new file mode 100644 index 0000000..f16fa4c --- /dev/null +++ b/nat-server/src/main/java/server/internal/handler/processor/LoginProcessor.java @@ -0,0 +1,137 @@ +package server.internal.handler.processor; + +import core.constant.FrameConstant; +import core.entity.Frame; +import core.entity.Tunnel; +import core.netty.group.ServerChannelGroup; +import core.netty.handler.processor.Processor; +import core.utils.BufUtil; +import core.utils.ByteUtil; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import lombok.extern.slf4j.Slf4j; +import server.internal.handler.processor.constant.ProcessorEnum; +import server.proxy.ProxyNettyServer; + +import java.nio.charset.StandardCharsets; +import java.util.*; + +/** + * @Author wneck130@gmail.com + * @Function 接入命令处理器(cmd:0x01),命令由internalClient发起请求,internalServer回复响应 + */ +@Slf4j +public class LoginProcessor implements Processor { + + private final String DATA_KEY_PASSWORD = "password"; + + private final String DATA_KEY_TUNNELS = "tunnels"; + + private final String DEFAULT_PASSWORD = "123456"; + + /** + * login数据帧处理 + * @param in netty获取的TCP数据流 + * @return + * @throws Exception + */ + @Override + public Frame assemble(ByteBuf in) throws Exception { + try { + //解析协议公共部分 + Frame frame = new Frame().quickHead(in); + //解析协议data部分 + byte passwordLen = in.readByte(); + byte[] passwordBytes = new byte[passwordLen & 0xFF]; + in.readBytes(passwordBytes); + String password = new String(passwordBytes, StandardCharsets.UTF_8); + List tunnelList = new ArrayList<>(); + while (in.readableBytes() > 0) { + int serverPort = in.readUnsignedShort(); + int clientHostSegment1 = in.readByte() & 0xFF; + int clientHostSegment2 = in.readByte() & 0xFF; + int clientHostSegment3 = in.readByte() & 0xFF; + int clientHostSegment4 = in.readByte() & 0xFF; + String clientHost = new StringBuilder().append(clientHostSegment1) + .append(".") + .append(clientHostSegment2) + .append(".") + .append(clientHostSegment3) + .append(".") + .append(clientHostSegment4).toString(); + int clientPort = in.readUnsignedShort(); + Tunnel tunnel = new Tunnel(); + tunnel.setServerPort(serverPort); + tunnel.setClientHost(clientHost); + tunnel.setClientPort(clientPort); + tunnelList.add(tunnel); + } + LinkedHashMap dataMap = new LinkedHashMap<>(2); + dataMap.put(DATA_KEY_PASSWORD, password); + dataMap.put(DATA_KEY_TUNNELS, tunnelList); + frame.setData(dataMap); + return frame; + } catch (Exception e) { + log.error("无法解析的消息: " + ByteUtil.toHexString(BufUtil.getArray(in))); + throw new UnknownFormatConversionException("无法解析的消息!!!"); + } + } + + /** + * login消息处理业务 + * @param ctx netty channel上下文 + * @param msg 解析成Frame结构的TCP请求数据帧 + * @throws Exception + */ + @Override + public void request(ChannelHandlerContext ctx, Frame msg) throws Exception { + Map requestData = msg.getData(); + //响应消息 + Frame response = new Frame(); + response.setPv(FrameConstant.RES_PV); + response.setSerial(msg.getSerial()); + response.setRes(msg.getRes()); + response.setReq(msg.getReq()); + response.setCmd(ProcessorEnum.LOGIN.getCmd()); + LinkedHashMap responseData = new LinkedHashMap<>(1); + responseData.put("result", FrameConstant.RESULT_SUCCESS); + response.setData(responseData); + String password = (String) requestData.get(DATA_KEY_PASSWORD); + //密码校验失败,直接返回错误信息并关闭channel + if (!Objects.equals(DEFAULT_PASSWORD, password)) { + responseData.put("result", FrameConstant.RESULT_FAIL); + ctx.writeAndFlush(response); + ctx.close(); + return; + } + List tunnels = (List) requestData.get(DATA_KEY_TUNNELS); + tunnels.forEach(tunnel -> { + if (ServerChannelGroup.tunnelExists(tunnel)) { + return; + } + new Thread(() -> { + ProxyNettyServer proxyNettyServer = new ProxyNettyServer(); + proxyNettyServer.start(tunnel.getServerPort()); + proxyNettyServer.getNioServerFuture().addListener((future) -> { + if (future.isSuccess()) { + //端口监听创建成功后,缓存proxyNettyServer和tunnel信息 + ServerChannelGroup.addProxyServers(proxyNettyServer.getNioServerFuture().channel(),tunnel); + log.debug("成功创建tunnel:[serverPort:{}], [ClientHost{}], [ClientPort:{}]", + tunnel.getServerPort(), tunnel.getClientHost(), tunnel.getClientPort()); + } + }); + }).start(); + }); + ctx.writeAndFlush(response); + } + + /** + * processor对应的命令字适配 + * @param cmd + * @return + */ + @Override + public boolean supply(byte cmd) { + return Objects.equals(ProcessorEnum.LOGIN.getCmd(), cmd); + } +} diff --git a/nat-server/src/main/java/server/internal/handler/processor/PreConnectProcessor.java b/nat-server/src/main/java/server/internal/handler/processor/PreConnectProcessor.java new file mode 100644 index 0000000..8903298 --- /dev/null +++ b/nat-server/src/main/java/server/internal/handler/processor/PreConnectProcessor.java @@ -0,0 +1,80 @@ +package server.internal.handler.processor; + +import core.netty.group.channel.message.ResponseEvent; +import core.constant.FrameConstant; +import core.entity.Frame; +import core.netty.handler.processor.Processor; +import core.netty.group.ServerChannelGroup; +import core.utils.BufUtil; +import core.utils.ByteUtil; +import io.netty.buffer.ByteBuf; +import io.netty.channel.Channel; +import lombok.extern.slf4j.Slf4j; +import server.internal.handler.processor.constant.ProcessorEnum; + +import java.util.LinkedHashMap; +import java.util.Objects; +import java.util.UnknownFormatConversionException; + +/** + * @author wneck130@gmail.com + * @Description: 预创建连接命令处理器(cmd:0x03),命令由proxyServer发起请求,proxyClient回复响应 + * @date 2021/9/27 + */ +@Slf4j +public class PreConnectProcessor implements Processor { + + /** + * preConnect数据帧组装,服务端处理的是响应命令 + * @param in netty获取的TCP数据流 + * @return + * @throws Exception + */ + @Override + public Frame assemble(ByteBuf in) throws Exception { + try { + //解析协议公共部分 + Frame frame = new Frame().quickHead(in); + byte result = in.readByte(); + LinkedHashMap dataMap = new LinkedHashMap<>(1); + dataMap.put("result", result); + frame.setData(dataMap); + return frame; + } catch (Exception e) { + log.error("无法解析的消息: " + ByteUtil.toHexString(BufUtil.getArray(in))); + throw new UnknownFormatConversionException("无法解析的消息!!!"); + } + } + + /** + * 处理预创建连接的响应消息 + * @param responseEvent + */ + @Override + public void response(ResponseEvent responseEvent) { + //处理响应信息 + byte result = (byte)responseEvent.getResponse().getData().get("result"); + if (result == FrameConstant.RESULT_SUCCESS) { + //成功后缓存配对关系 + ServerChannelGroup.addChannelPair(responseEvent.getResponse().getReq(), + responseEvent.getResponse().getRes()); + } + } + + @Override + public void timeout(ResponseEvent responseEvent) { + try { + //超时后默认进行一次重试 + Channel internalChannel = ServerChannelGroup.forkChannel(responseEvent.getChannelId()); + internalChannel.writeAndFlush(responseEvent.getRequest()); + } catch (Exception e) { + log.error("channel[{}]处理超时指令CMD:{},SERIAL:{}重试失败", responseEvent.getChannelId(), + responseEvent.getRequest().getCmd(), responseEvent.getRequest().getSerial()); + } + } + + @Override + public boolean supply(byte cmd) { + return Objects.equals(ProcessorEnum.PRE_CONNECT.getCmd(), cmd); + } +} diff --git a/nat-server/src/main/java/server/internal/handler/processor/UpStreamProcessor.java b/nat-server/src/main/java/server/internal/handler/processor/UpStreamProcessor.java new file mode 100644 index 0000000..7c3d4b7 --- /dev/null +++ b/nat-server/src/main/java/server/internal/handler/processor/UpStreamProcessor.java @@ -0,0 +1,77 @@ +package server.internal.handler.processor; + +import core.constant.FrameConstant; +import core.entity.Frame; +import core.netty.group.ServerChannelGroup; +import core.netty.handler.processor.Processor; +import core.utils.BufUtil; +import core.utils.ByteUtil; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import lombok.extern.slf4j.Slf4j; +import server.internal.handler.processor.constant.ProcessorEnum; + +import java.util.LinkedHashMap; +import java.util.Objects; +import java.util.UnknownFormatConversionException; + +/** + * @author wneck130@gmail.com + * @Description: 上行数据处理器(cmd:0xFF),命令由proxyClient发起请求,proxyServer回复响应 + * @date 2021/9/29 + */ +@Slf4j +public class UpStreamProcessor implements Processor { + + @Override + public Frame assemble(ByteBuf in) throws Exception { + try { + Frame frame = new Frame().quickHead(in); + //解析协议data部分 + byte[] dataBytes = new byte[in.readableBytes()]; + in.readBytes(dataBytes); + //生成数据帧 + LinkedHashMap dataMap = new LinkedHashMap<>(); + dataMap.put("data", dataBytes); + frame.setData(dataMap); + return frame; + } catch (Exception e) { + log.error("无法解析的消息: " + ByteUtil.toHexString(BufUtil.getArray(in))); + throw new UnknownFormatConversionException("无法解析的消息!!!"); + } + } + + @Override + public void request(ChannelHandlerContext ctx, Frame msg) throws Exception { + //将消息转发给对应的外部请求方 + Channel proxyChannel = ServerChannelGroup.findProxy(msg.getRes()); + ByteBuf upStream = Unpooled.buffer(); + upStream.writeBytes((byte[])msg.getData().get("data")); + proxyChannel.writeAndFlush(upStream).addListener(future -> { + //根据发送结果回写响应 + Frame response = new Frame(); + response.setPv(FrameConstant.RES_PV); + response.setSerial(msg.getSerial()); + response.setCmd(msg.getCmd()); + response.setLen(FrameConstant.FRAME_RESULT_LEN); + LinkedHashMap dataMap = new LinkedHashMap<>(1); + proxyChannel.writeAndFlush(response); + if (future.isSuccess()) { + dataMap.put("result", FrameConstant.RESULT_SUCCESS); + } else { + log.error("向外部请求发送数据失败:", future.cause()); + dataMap.put("result", FrameConstant.RESULT_FAIL); + } + response.setData(dataMap); + Channel internalChannel = ServerChannelGroup.forkChannel(proxyChannel.id().asShortText()); + internalChannel.writeAndFlush(response); + }); + } + + @Override + public boolean supply(byte cmd) { + return Objects.equals(ProcessorEnum.UP_STREAM.getCmd(), cmd); + } +} diff --git a/nat-server/src/main/java/server/internal/handler/processor/constant/ProcessorEnum.java b/nat-server/src/main/java/server/internal/handler/processor/constant/ProcessorEnum.java new file mode 100644 index 0000000..36d0a02 --- /dev/null +++ b/nat-server/src/main/java/server/internal/handler/processor/constant/ProcessorEnum.java @@ -0,0 +1,66 @@ +package server.internal.handler.processor.constant; + +import core.netty.handler.processor.Processor; +import server.internal.handler.processor.*; + +import java.util.Objects; + +/** + * @author wneck130@gmail.com + * @Description: + * @date 2021/9/24 + */ +public enum ProcessorEnum { + /** + * 登录处理器 + */ + LOGIN((byte)0x01, LoginProcessor.class), + /** + * 心跳处理器 + */ + HEARTBEAT((byte)0x02, HeartbeatProcessor.class), + /** + * 预创建连接 + */ + PRE_CONNECT((byte)0x03, PreConnectProcessor.class), + /** + * 数据下发处理器 + */ + DOWN_STREAM((byte)0xEE, DownStreamProcessor.class), + /** + * 数据上行处理器 + */ + UP_STREAM((byte)0xFF, UpStreamProcessor.class), + ; + + private byte cmd; + + private Class clazz; + + ProcessorEnum(byte cmd, Class clazz) { + this.cmd = cmd; + this.clazz = clazz; + } + + public byte getCmd() { + return cmd; + } + + public Class getClazz() { + return clazz; + } + + /** + * 根据cmd获取对应class对象 + * @param cmd + * @return + */ + public static Class getClassByCmd(byte cmd) { + for (ProcessorEnum processorEnum : ProcessorEnum.values()) { + if (Objects.equals(processorEnum.getCmd(), cmd)) { + return processorEnum.getClazz(); + } + } + throw new EnumConstantNotPresentException(ProcessorEnum.class, cmd + "枚举常量不存在"); + } +} diff --git a/nat-server/src/main/java/server/proxy/ProxyNettyServer.java b/nat-server/src/main/java/server/proxy/ProxyNettyServer.java new file mode 100644 index 0000000..b905ade --- /dev/null +++ b/nat-server/src/main/java/server/proxy/ProxyNettyServer.java @@ -0,0 +1,126 @@ +package server.proxy; + +import core.properties.cache.PropertiesCache; +import core.constant.FrameConstant; +import core.netty.handler.DispatcherHandler; +import core.netty.stater.server.BaseServer; +import core.netty.stater.server.NettyServer; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.handler.codec.http.HttpObjectAggregator; +import io.netty.handler.codec.http.HttpServerCodec; +import io.netty.handler.timeout.IdleStateHandler; +import lombok.extern.slf4j.Slf4j; +import server.proxy.handler.HttpProxyServerHandler; +import server.proxy.handler.TcpInLogHandler; +import server.proxy.handler.TcpOutLogHandler; +import server.proxy.handler.TcpProxyServerHandler; + +import java.util.concurrent.TimeUnit; + +/** + * @author wneck130@gmail.com + * @Description: + * @date 2021/9/26 + */ +@Slf4j +public class ProxyNettyServer extends BaseServer implements NettyServer { + + @Override + public void init() { + cache = PropertiesCache.getInstance(); + genericFutureListener = (ch) -> { + nioServerFuture.channel().eventLoop() + .schedule(() -> this.start(port), 10, TimeUnit.SECONDS); + }; + addShutdownHook(); + } + + @Override + public void start(int port) { + init(); + this.port = port; + //如果已经启动了proxyServer,静默 + if (nioServerFuture != null && nioServerFuture.channel().isOpen()) { + return; + } + try { + bossGroup = new NioEventLoopGroup(FrameConstant.BOSSGROUP_NUM); + workerGroup = new NioEventLoopGroup(); + ServerBootstrap b = new ServerBootstrap(); + ChannelInitializer channelInit = new ChannelInitializer() { + @Override + protected void initChannel(SocketChannel ch) throws Exception { + ch.pipeline() + //闲置连接回收 + .addLast(new IdleStateHandler(READ_IDLE, WRITE_IDLE, ALL_IDLE, TimeUnit.MINUTES)) + //inbound流日志记录 + .addLast(new TcpInLogHandler()) + //outbound流日志记录 + .addLast(new TcpOutLogHandler()) + //业务处理 + .addLast(new TcpProxyServerHandler()); +// .addLast("dispatcher", new DispatcherHandler() { +// @Override +// public void addHttpHandler(ChannelHandlerContext ctx) { +// // server端发送的是httpResponse,所以要使用HttpResponseEncoder进行编码 +// ch.pipeline().addLast(new HttpServerCodec()) +// .addLast(new HttpObjectAggregator(512 * 1024)) +// .addLast(new HttpProxyServerHandler()); +// } +// +// @Override +// public void addTcpHandler(ChannelHandlerContext ctx) { +// ctx.pipeline() +// //闲置连接回收 +// .addLast(new IdleStateHandler(READ_IDLE, WRITE_IDLE, ALL_IDLE, TimeUnit.MINUTES)) +// //inbound流日志记录 +// .addLast(new TcpInLogHandler()) +// //outbound流日志记录 +// .addLast(new TcpOutLogHandler()) +// //业务处理 +// .addLast(new TcpProxyServerHandler()); +// } +// }) + + } + }; + b.group(bossGroup, workerGroup) + .channel(NioServerSocketChannel.class) + .option(ChannelOption.SO_BACKLOG, FrameConstant.TCP_SO_BACKLOG) + .option(ChannelOption.SO_REUSEADDR, FrameConstant.TCP_REUSE_ADDR) + .childHandler(channelInit) + .childOption(ChannelOption.TCP_NODELAY, Boolean.TRUE); + + nioServerFuture = b.bind(port).sync(); + log.debug("ProxyServer started on port {}......", port); + //添加关闭重启的监听器,3秒后尝试重启 + nioServerFuture.channel().closeFuture().addListener(genericFutureListener); +// nioServerFuture.channel().closeFuture().sync(); + } catch (Exception e) { + log.error("ProxyServer started failed while listening on port {}!!!", port, e); + } + } + + @Override + public boolean isHeathy() { + return nioServerFuture.channel().isOpen(); + } + + @Override + public boolean isRunning() { + return nioServerFuture.channel().isActive(); + } + + @Override + public void close() { + nioServerFuture.removeListener(genericFutureListener); + nioServerFuture.channel().close(); + log.info("ProxyServer closed success"); + } +} diff --git a/nat-server/src/main/java/server/proxy/handler/HttpProxyServerHandler.java b/nat-server/src/main/java/server/proxy/handler/HttpProxyServerHandler.java new file mode 100644 index 0000000..dc1f963 --- /dev/null +++ b/nat-server/src/main/java/server/proxy/handler/HttpProxyServerHandler.java @@ -0,0 +1,35 @@ +package server.proxy.handler; + +import core.netty.group.ServerChannelGroup; +import core.netty.group.channel.strategy.constant.ForkStrategyEnum; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.handler.codec.http.DefaultFullHttpResponse; +import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.HttpResponseStatus; +import io.netty.handler.codec.http.HttpVersion; + +import static io.netty.handler.codec.http.HttpUtil.is100ContinueExpected; + +/** + * @author wneck130@gmail.com + * @Description: + * @date 2021/9/27 + */ +public class HttpProxyServerHandler extends SimpleChannelInboundHandler { + + @Override + protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception { + //处理100 continue请求 + if (is100ContinueExpected(msg)) { + ctx.write(new DefaultFullHttpResponse( + HttpVersion.HTTP_1_1, + HttpResponseStatus.CONTINUE)); + } + //收到外部的msg消息,首先挑选一条internalChannel准备进行数据转发 + Channel internalChannel = ServerChannelGroup.forkChannel(ForkStrategyEnum.MIN_LOAD); + //TODO 可在此添加代码修改http请求 + internalChannel.writeAndFlush(msg); + } +} diff --git a/nat-server/src/main/java/server/proxy/handler/TcpInLogHandler.java b/nat-server/src/main/java/server/proxy/handler/TcpInLogHandler.java new file mode 100644 index 0000000..1b4c955 --- /dev/null +++ b/nat-server/src/main/java/server/proxy/handler/TcpInLogHandler.java @@ -0,0 +1,58 @@ +package server.proxy.handler; + +import core.entity.Frame; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import lombok.extern.slf4j.Slf4j; + +import java.net.InetSocketAddress; + +/** + * @author wneck130@gmail.com + * @Description: + * @date 2021/9/27 + */ +@Slf4j +public class TcpInLogHandler extends SimpleChannelInboundHandler { + + private String clientIP; + + private String clientPort; + + @Override + protected void channelRead0(ChannelHandlerContext ctx, Frame msg) throws Exception { + log.debug("TcpProxyServer收到消息:{}", msg.toString()); + ctx.fireChannelRead(msg); + } + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress(); + clientIP = insocket.getAddress().getHostAddress(); + clientPort = String.valueOf(insocket.getPort()); + log.debug("收到来自{}:{}的请求,成功创建Channel[{}]", clientIP, clientPort, ctx.channel().id()); + super.channelActive(ctx); + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + log.debug("关闭来自{}:{}的请求,成功回收Channel[{}]", clientIP, clientPort, ctx.channel().id()); + super.channelInactive(ctx); + } + + /** + * 通道异常触发 + * @param ctx + * @param cause + * @throws Exception + */ + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + Channel channel = ctx.channel(); + if(!channel.isActive()){ + ctx.close(); + } + log.error("TcpProxyServer连接[{}]发生异常:{}", ctx.channel().id(), cause.getStackTrace()); + } +} diff --git a/nat-server/src/main/java/server/proxy/handler/TcpOutLogHandler.java b/nat-server/src/main/java/server/proxy/handler/TcpOutLogHandler.java new file mode 100644 index 0000000..d993c47 --- /dev/null +++ b/nat-server/src/main/java/server/proxy/handler/TcpOutLogHandler.java @@ -0,0 +1,24 @@ +package server.proxy.handler; + +import core.utils.BufUtil; +import core.utils.ByteUtil; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelOutboundHandlerAdapter; +import io.netty.channel.ChannelPromise; +import lombok.extern.slf4j.Slf4j; + +/** + * @author wneck130@gmail.com + * @Description: + * @date 2021/9/27 + */ +@Slf4j +public class TcpOutLogHandler extends ChannelOutboundHandlerAdapter { + + @Override + public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { + log.debug("ProxyServer发送消息:{}", ByteUtil.toHexString(BufUtil.getArray((ByteBuf) msg))); + super.write(ctx, msg, promise); + } +} diff --git a/nat-server/src/main/java/server/proxy/handler/TcpProxyServerHandler.java b/nat-server/src/main/java/server/proxy/handler/TcpProxyServerHandler.java new file mode 100644 index 0000000..fef0ae0 --- /dev/null +++ b/nat-server/src/main/java/server/proxy/handler/TcpProxyServerHandler.java @@ -0,0 +1,111 @@ +package server.proxy.handler; + +import core.constant.FrameConstant; +import core.entity.Frame; +import core.entity.Tunnel; +import core.netty.group.ServerChannelGroup; +import core.netty.group.channel.strategy.constant.ForkStrategyEnum; +import core.utils.ByteUtil; +import io.netty.buffer.ByteBuf; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import lombok.extern.slf4j.Slf4j; +import server.internal.handler.processor.constant.ProcessorEnum; + +import java.net.InetSocketAddress; +import java.util.LinkedHashMap; + +/** + * @Author wneck130@gmail.com + * @Function proxyServer业务handler + */ +@Slf4j +public class TcpProxyServerHandler extends SimpleChannelInboundHandler { + + /** + * 数据传输时触发 + * + * @param ctx + * @param msg + * @throws Exception + */ + @Override + protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { + //收到外部的msg消息,首先挑选一条internalChannel准备进行数据转发 + String serverChannelId = ctx.channel().id().asShortText(); + String clientChannelId = ServerChannelGroup.getClientByServer(serverChannelId); + //没有对应的服务端channel,直接返回 + if (clientChannelId.isEmpty()) { + long start = System.currentTimeMillis(); + while (System.currentTimeMillis() - start < 5000) { + clientChannelId = ServerChannelGroup.getClientByServer(serverChannelId); + if (!clientChannelId.isEmpty()) { + break; + } + Thread.sleep(200); + } + if (clientChannelId.isEmpty()) { + ctx.close(); + return; + } + } + Channel internalChannel = ServerChannelGroup.forkChannel(ForkStrategyEnum.MIN_LOAD); + //第二步,封装成内部通信的指定格式 + byte[] message = new byte[msg.readableBytes()]; + msg.readBytes(message); + LinkedHashMap map = new LinkedHashMap<>(1); + map.put("data", message); + Frame frame = new Frame(); + frame.setCmd(ProcessorEnum.DOWN_STREAM.getCmd()); + frame.setReq(serverChannelId); + frame.setRes(clientChannelId); + frame.setData(map); + internalChannel.writeAndFlush(frame); + } + + /** + * 通道连接成功触发 + * + * @param ctx + * @throws Exception + */ + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + InetSocketAddress inetSocketAddress = (InetSocketAddress) ctx.channel().localAddress(); + log.debug("{}:{}收到新的连接请求", inetSocketAddress.getHostName(), inetSocketAddress.getPort()); + ServerChannelGroup.addProxy(ctx.channel()); + //新的连接建立后进行配对 + fullyConnect(ctx); + ServerChannelGroup.printGroupState(); + } + + /** + * 外部请求与ProxyServer激活channel连接时,通知ProxyClient与被代理服务预建立连接 + */ + public void fullyConnect(ChannelHandlerContext ctx) throws Exception { + //发送启动代理客户端命令 + Frame frame = new Frame(); + frame.setReq(ctx.channel().id().asShortText()); + frame.setRes(FrameConstant.DEFAULT_CHANNEL_ID); + frame.setCmd(ProcessorEnum.PRE_CONNECT.getCmd()); + //通过当前channel的parent channel获取对应的tunnelId + Tunnel tunnel = ServerChannelGroup.getTunnelByChannel(ctx.channel().parent()); + Channel internalChannel = ServerChannelGroup.forkChannel(ForkStrategyEnum.MIN_LOAD); + LinkedHashMap data = new LinkedHashMap<>(1); + String[] clientHostSegment = tunnel.getClientHost().split("\\."); + data.put("host", new byte[]{ByteUtil.fromInt(Integer.parseInt(clientHostSegment[0]))[3], + ByteUtil.fromInt(Integer.parseInt(clientHostSegment[1]))[3], + ByteUtil.fromInt(Integer.parseInt(clientHostSegment[2]))[3], + ByteUtil.fromInt(Integer.parseInt(clientHostSegment[3]))[3]}); + data.put("port", new byte[]{ByteUtil.fromInt(tunnel.getClientPort())[2], + ByteUtil.fromInt(tunnel.getClientPort())[3]}); + frame.setData(data); + internalChannel.writeAndFlush(frame); + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + ServerChannelGroup.removeProxy(ctx.channel()); + } +} diff --git a/nat-server/src/main/resources/properties.yml b/nat-server/src/main/resources/properties.yml new file mode 100644 index 0000000..0951d58 --- /dev/null +++ b/nat-server/src/main/resources/properties.yml @@ -0,0 +1,4 @@ +#内部通信使用的端口,需要与客户端的internal.server.port值保持一致 +internal: + server: + port: 8083 \ No newline at end of file diff --git a/nat-server/properties.properties b/nat-server/target/archive-tmp/fileSetFormatter.924177572.tmp/properties.properties similarity index 100% rename from nat-server/properties.properties rename to nat-server/target/archive-tmp/fileSetFormatter.924177572.tmp/properties.properties diff --git a/nat-server/target/archive-tmp/fileSetFormatter.924177572.tmp/properties.properties.1498542851.filtered b/nat-server/target/archive-tmp/fileSetFormatter.924177572.tmp/properties.properties.1498542851.filtered new file mode 100644 index 0000000..404359a --- /dev/null +++ b/nat-server/target/archive-tmp/fileSetFormatter.924177572.tmp/properties.properties.1498542851.filtered @@ -0,0 +1,5 @@ +proxy.server.port=80 +internal.server.port=8080 +internal.channel.init.num=100 +internal.channel.max.idle.num=100 +internal.channel.min.idle.num=5 diff --git a/nat-server/target/archive-tmp/fileSetFormatter.924177572.tmp/properties.yml b/nat-server/target/archive-tmp/fileSetFormatter.924177572.tmp/properties.yml new file mode 100644 index 0000000..e69de29 diff --git a/nat-server/target/archive-tmp/fileSetFormatter.924177572.tmp/properties.yml.1736769126.filtered b/nat-server/target/archive-tmp/fileSetFormatter.924177572.tmp/properties.yml.1736769126.filtered new file mode 100644 index 0000000..e69de29 diff --git a/nat-server/target/archive-tmp/fileSetFormatter.943498776.tmp/log4j.properties b/nat-server/target/archive-tmp/fileSetFormatter.943498776.tmp/log4j.properties new file mode 100644 index 0000000..f8b1339 --- /dev/null +++ b/nat-server/target/archive-tmp/fileSetFormatter.943498776.tmp/log4j.properties @@ -0,0 +1,24 @@ +### ### +log4j.rootLogger = debug,stdout,D,E + +### Ϣ̧ ### +log4j.appender.stdout = org.apache.log4j.ConsoleAppender +log4j.appender.stdout.Target = System.out +log4j.appender.stdout.layout = org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n + +### DEBUG ϵ־=E://logs/error.log ### +log4j.appender.D = org.apache.log4j.DailyRollingFileAppender +log4j.appender.D.File = E://netty-log/log.log +log4j.appender.D.Append = true +log4j.appender.D.Threshold = DEBUG +log4j.appender.D.layout = org.apache.log4j.PatternLayout +log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n + +### ERROR ϵ־=E://logs/error.log ### +log4j.appender.E = org.apache.log4j.DailyRollingFileAppender +log4j.appender.E.File =E://netty-log/error.log +log4j.appender.E.Append = true +log4j.appender.E.Threshold = ERROR +log4j.appender.E.layout = org.apache.log4j.PatternLayout +log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n \ No newline at end of file diff --git a/nat-server/target/archive-tmp/fileSetFormatter.943498776.tmp/log4j.properties.1137501801.filtered b/nat-server/target/archive-tmp/fileSetFormatter.943498776.tmp/log4j.properties.1137501801.filtered new file mode 100644 index 0000000..f8b1339 --- /dev/null +++ b/nat-server/target/archive-tmp/fileSetFormatter.943498776.tmp/log4j.properties.1137501801.filtered @@ -0,0 +1,24 @@ +### ### +log4j.rootLogger = debug,stdout,D,E + +### Ϣ̧ ### +log4j.appender.stdout = org.apache.log4j.ConsoleAppender +log4j.appender.stdout.Target = System.out +log4j.appender.stdout.layout = org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n + +### DEBUG ϵ־=E://logs/error.log ### +log4j.appender.D = org.apache.log4j.DailyRollingFileAppender +log4j.appender.D.File = E://netty-log/log.log +log4j.appender.D.Append = true +log4j.appender.D.Threshold = DEBUG +log4j.appender.D.layout = org.apache.log4j.PatternLayout +log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n + +### ERROR ϵ־=E://logs/error.log ### +log4j.appender.E = org.apache.log4j.DailyRollingFileAppender +log4j.appender.E.File =E://netty-log/error.log +log4j.appender.E.Append = true +log4j.appender.E.Threshold = ERROR +log4j.appender.E.layout = org.apache.log4j.PatternLayout +log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n \ No newline at end of file diff --git a/nat-server/target/classes/log4j.properties b/nat-server/target/classes/log4j.properties new file mode 100644 index 0000000..f8b1339 --- /dev/null +++ b/nat-server/target/classes/log4j.properties @@ -0,0 +1,24 @@ +### ### +log4j.rootLogger = debug,stdout,D,E + +### Ϣ̧ ### +log4j.appender.stdout = org.apache.log4j.ConsoleAppender +log4j.appender.stdout.Target = System.out +log4j.appender.stdout.layout = org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n + +### DEBUG ϵ־=E://logs/error.log ### +log4j.appender.D = org.apache.log4j.DailyRollingFileAppender +log4j.appender.D.File = E://netty-log/log.log +log4j.appender.D.Append = true +log4j.appender.D.Threshold = DEBUG +log4j.appender.D.layout = org.apache.log4j.PatternLayout +log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n + +### ERROR ϵ־=E://logs/error.log ### +log4j.appender.E = org.apache.log4j.DailyRollingFileAppender +log4j.appender.E.File =E://netty-log/error.log +log4j.appender.E.Append = true +log4j.appender.E.Threshold = ERROR +log4j.appender.E.layout = org.apache.log4j.PatternLayout +log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n \ No newline at end of file diff --git a/nat-server/target/classes/properties.yml b/nat-server/target/classes/properties.yml new file mode 100644 index 0000000..0951d58 --- /dev/null +++ b/nat-server/target/classes/properties.yml @@ -0,0 +1,4 @@ +#内部通信使用的端口,需要与客户端的internal.server.port值保持一致 +internal: + server: + port: 8083 \ No newline at end of file diff --git a/nat-server/target/libs/jackson-annotations-2.11.0.jar b/nat-server/target/libs/jackson-annotations-2.11.0.jar new file mode 100644 index 0000000..2c34567 Binary files /dev/null and b/nat-server/target/libs/jackson-annotations-2.11.0.jar differ diff --git a/nat-server/target/libs/jackson-core-2.11.0.jar b/nat-server/target/libs/jackson-core-2.11.0.jar new file mode 100644 index 0000000..6ed89ba Binary files /dev/null and b/nat-server/target/libs/jackson-core-2.11.0.jar differ diff --git a/nat-server/target/libs/jackson-databind-2.11.0.jar b/nat-server/target/libs/jackson-databind-2.11.0.jar new file mode 100644 index 0000000..458e1d3 Binary files /dev/null and b/nat-server/target/libs/jackson-databind-2.11.0.jar differ diff --git a/nat-server/target/libs/jackson-dataformat-yaml-2.12.5.jar b/nat-server/target/libs/jackson-dataformat-yaml-2.12.5.jar new file mode 100644 index 0000000..52b0896 Binary files /dev/null and b/nat-server/target/libs/jackson-dataformat-yaml-2.12.5.jar differ diff --git a/nat-server/target/libs/log4j-1.2.17.jar b/nat-server/target/libs/log4j-1.2.17.jar new file mode 100644 index 0000000..1d425cf Binary files /dev/null and b/nat-server/target/libs/log4j-1.2.17.jar differ diff --git a/nat-server/target/libs/log4j-api-2.13.2.jar b/nat-server/target/libs/log4j-api-2.13.2.jar new file mode 100644 index 0000000..afc89ff Binary files /dev/null and b/nat-server/target/libs/log4j-api-2.13.2.jar differ diff --git a/nat-server/target/libs/log4j-core-2.13.2.jar b/nat-server/target/libs/log4j-core-2.13.2.jar new file mode 100644 index 0000000..9b6bdff Binary files /dev/null and b/nat-server/target/libs/log4j-core-2.13.2.jar differ diff --git a/nat-server/target/libs/lombok-1.18.20.jar b/nat-server/target/libs/lombok-1.18.20.jar new file mode 100644 index 0000000..d3434f6 Binary files /dev/null and b/nat-server/target/libs/lombok-1.18.20.jar differ diff --git a/nat-server/target/libs/nat-core-1.0-SNAPSHOT.jar b/nat-server/target/libs/nat-core-1.0-SNAPSHOT.jar new file mode 100644 index 0000000..14aab9a Binary files /dev/null and b/nat-server/target/libs/nat-core-1.0-SNAPSHOT.jar differ diff --git a/nat-server/target/libs/netty-all-4.1.49.Final.jar b/nat-server/target/libs/netty-all-4.1.49.Final.jar new file mode 100644 index 0000000..6ecb025 Binary files /dev/null and b/nat-server/target/libs/netty-all-4.1.49.Final.jar differ diff --git a/nat-server/target/libs/slf4j-api-1.7.21.jar b/nat-server/target/libs/slf4j-api-1.7.21.jar new file mode 100644 index 0000000..2a5c33e Binary files /dev/null and b/nat-server/target/libs/slf4j-api-1.7.21.jar differ diff --git a/nat-server/target/libs/slf4j-log4j12-1.7.21.jar b/nat-server/target/libs/slf4j-log4j12-1.7.21.jar new file mode 100644 index 0000000..ff4fddd Binary files /dev/null and b/nat-server/target/libs/slf4j-log4j12-1.7.21.jar differ diff --git a/nat-server/target/libs/snakeyaml-1.27.jar b/nat-server/target/libs/snakeyaml-1.27.jar new file mode 100644 index 0000000..79bd430 Binary files /dev/null and b/nat-server/target/libs/snakeyaml-1.27.jar differ diff --git a/nat-server/target/maven-archiver/pom.properties b/nat-server/target/maven-archiver/pom.properties new file mode 100644 index 0000000..80130ad --- /dev/null +++ b/nat-server/target/maven-archiver/pom.properties @@ -0,0 +1,4 @@ +#Created by Apache Maven 3.8.1 +version=1.0-SNAPSHOT +groupId=com.scrat +artifactId=nat-server diff --git a/nat-server/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/nat-server/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file mode 100644 index 0000000..9213f10 --- /dev/null +++ b/nat-server/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -0,0 +1,20 @@ +server\internal\handler\CustomEventHandler.class +server\internal\handler\processor\PreConnectProcessor.class +server\internal\InternalNettyServer$1.class +server\internal\handler\HttpHandler.class +server\internal\handler\InternalServerHandler.class +server\internal\handler\processor\constant\ProcessorEnum.class +server\proxy\handler\TcpOutLogHandler.class +server\proxy\ProxyNettyServer.class +server\proxy\handler\HttpProxyServerHandler.class +server\internal\handler\processor\LoginProcessor.class +server\proxy\ProxyNettyServer$1.class +server\internal\handler\processor\HeartbeatProcessor.class +server\proxy\handler\TcpProxyServerHandler.class +server\internal\InternalNettyServer.class +server\proxy\handler\TcpInLogHandler.class +server\internal\decoder\PojoToByteEncoder.class +server\internal\decoder\ByteToPojoDecoder.class +server\internal\handler\processor\DownStreamProcessor.class +server\internal\handler\ConverterHandler.class +server\internal\handler\processor\UpStreamProcessor.class diff --git a/nat-server/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/nat-server/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file mode 100644 index 0000000..237c673 --- /dev/null +++ b/nat-server/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -0,0 +1,20 @@ +E:\workspace\netty-nat\nat-server\src\main\java\server\proxy\handler\TcpOutLogHandler.java +E:\workspace\netty-nat\nat-server\src\main\java\server\internal\handler\processor\ConnectionExpandProcessor.java +E:\workspace\netty-nat\nat-server\src\main\java\server\internal\handler\processor\HeartbeatProcessor.java +E:\workspace\netty-nat\nat-server\src\main\java\server\internal\InternalNettyServer.java +E:\workspace\netty-nat\nat-server\src\main\java\server\proxy\handler\TcpInLogHandler.java +E:\workspace\netty-nat\nat-server\src\main\java\server\internal\handler\HttpHandler.java +E:\workspace\netty-nat\nat-server\src\main\java\server\internal\handler\processor\LoginProcessor.java +E:\workspace\netty-nat\nat-server\src\main\java\server\proxy\ProxyNettyServer.java +E:\workspace\netty-nat\nat-server\src\main\java\server\internal\handler\ConverterHandler.java +E:\workspace\netty-nat\nat-server\src\main\java\server\internal\handler\CustomEventHandler.java +E:\workspace\netty-nat\nat-server\src\main\java\server\proxy\handler\TcpProxyServerHandler.java +E:\workspace\netty-nat\nat-server\src\main\java\server\internal\decoder\PojoToByteEncoder.java +E:\workspace\netty-nat\nat-server\src\main\java\server\internal\handler\processor\ConnectionReduceProcessor.java +E:\workspace\netty-nat\nat-server\src\main\java\server\internal\handler\processor\UpStreamProcessor.java +E:\workspace\netty-nat\nat-server\src\main\java\server\internal\handler\InternalServerHandler.java +E:\workspace\netty-nat\nat-server\src\main\java\server\internal\handler\processor\constant\ProcessorEnum.java +E:\workspace\netty-nat\nat-server\src\main\java\server\internal\handler\processor\DownStreamProcessor.java +E:\workspace\netty-nat\nat-server\src\main\java\server\internal\handler\processor\PreConnectProcessor.java +E:\workspace\netty-nat\nat-server\src\main\java\server\proxy\handler\HttpProxyServerHandler.java +E:\workspace\netty-nat\nat-server\src\main\java\server\internal\decoder\ByteToPojoDecoder.java diff --git a/nat-server/target/nat-server-1.0-SNAPSHOT.jar b/nat-server/target/nat-server-1.0-SNAPSHOT.jar new file mode 100644 index 0000000..d54f54a Binary files /dev/null and b/nat-server/target/nat-server-1.0-SNAPSHOT.jar differ diff --git a/nat-server/target/nat-server-1.0-SNAPSHOT.zip b/nat-server/target/nat-server-1.0-SNAPSHOT.zip new file mode 100644 index 0000000..fc944d7 Binary files /dev/null and b/nat-server/target/nat-server-1.0-SNAPSHOT.zip differ diff --git "a/netty-nat\346\227\266\345\272\217\345\233\276.png" "b/netty-nat\346\227\266\345\272\217\345\233\276.png" deleted file mode 100644 index 2f9a304..0000000 Binary files "a/netty-nat\346\227\266\345\272\217\345\233\276.png" and /dev/null differ diff --git a/pom.xml b/pom.xml index d1f29e4..1782772 100644 --- a/pom.xml +++ b/pom.xml @@ -17,7 +17,7 @@ 4.1.49.Final 2.11.0 - 1.16.18 + 1.18.20 1.7.21 diff --git a/properties.properties b/properties.properties deleted file mode 100644 index 1fc7726..0000000 --- a/properties.properties +++ /dev/null @@ -1,13 +0,0 @@ -sys.server.port=8080 -internal.port=8080 -proxy.server.port=9000 -internal.server.port=8082 -internal.host=121.40.180.31 -proxy.client.port=81 -proxy.client.host=192.168.0.158 -##ӳСʣ -min.connection.pool=20 -#ӳ -max.connection.pool=100 -#ӳ -connection_pool_expansion=50 \ No newline at end of file diff --git "a/\344\273\243\347\220\206\347\250\213\345\272\217\351\200\232\344\277\241\345\215\217\350\256\256.docx" "b/\344\273\243\347\220\206\347\250\213\345\272\217\351\200\232\344\277\241\345\215\217\350\256\256.docx" deleted file mode 100644 index ae85927..0000000 Binary files "a/\344\273\243\347\220\206\347\250\213\345\272\217\351\200\232\344\277\241\345\215\217\350\256\256.docx" and /dev/null differ