diff --git a/pom.xml b/pom.xml
index 322864c..ac7c552 100644
--- a/pom.xml
+++ b/pom.xml
@@ -24,6 +24,7 @@
wu-lazy-cloud-heartbeat-client
wu-lazy-cloud-heartbeat-common
wu-lazy-cloud-heartbeat-dns
+ wu-lazy-cloud-heartbeat-protocol-proxy
wu-lazy-cloud-heartbeat-start
diff --git a/wu-lazy-cloud-heartbeat-client/pom.xml b/wu-lazy-cloud-heartbeat-client/pom.xml
index 1158de4..16cf6c7 100644
--- a/wu-lazy-cloud-heartbeat-client/pom.xml
+++ b/wu-lazy-cloud-heartbeat-client/pom.xml
@@ -23,6 +23,11 @@
top.wu2020
wu-lazy-cloud-heartbeat-common
+
+ top.wu2020
+ wu-lazy-cloud-heartbeat-protocol-proxy
+ 1.3.0-JDK17-SNAPSHOT
+
com.alibaba
fastjson
diff --git a/wu-lazy-cloud-heartbeat-common/src/main/java/org/framework/lazy/cloud/network/heartbeat/common/context/SocketApplicationListener.java b/wu-lazy-cloud-heartbeat-common/src/main/java/org/framework/lazy/cloud/network/heartbeat/common/context/SocketApplicationListener.java
index c7e68eb..a3928df 100644
--- a/wu-lazy-cloud-heartbeat-common/src/main/java/org/framework/lazy/cloud/network/heartbeat/common/context/SocketApplicationListener.java
+++ b/wu-lazy-cloud-heartbeat-common/src/main/java/org/framework/lazy/cloud/network/heartbeat/common/context/SocketApplicationListener.java
@@ -39,7 +39,7 @@ public interface SocketApplicationListener extends CommandLineRunner, Disposable
/**
* 运行
*
- * @throws InterruptedException
+ * @throws InterruptedException 异常信息
*/
void doRunning() throws Exception;
diff --git a/wu-lazy-cloud-heartbeat-dns/src/main/java/org/framework/lazy/cloud/network/heartbeat/dns/EnableDnsAutoConfiguration.java b/wu-lazy-cloud-heartbeat-dns/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/EnableDnsAutoConfiguration.java
similarity index 84%
rename from wu-lazy-cloud-heartbeat-dns/src/main/java/org/framework/lazy/cloud/network/heartbeat/dns/EnableDnsAutoConfiguration.java
rename to wu-lazy-cloud-heartbeat-dns/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/EnableDnsAutoConfiguration.java
index 5a6f52a..c62329b 100644
--- a/wu-lazy-cloud-heartbeat-dns/src/main/java/org/framework/lazy/cloud/network/heartbeat/dns/EnableDnsAutoConfiguration.java
+++ b/wu-lazy-cloud-heartbeat-dns/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/EnableDnsAutoConfiguration.java
@@ -1,4 +1,4 @@
-package org.framework.lazy.cloud.network.heartbeat.dns;
+package org.framework.lazy.cloud.network.heartbeat.protocol;
import org.springframework.context.annotation.ComponentScan;
import org.wu.framework.lazy.orm.core.stereotype.LazyScan;
@@ -7,6 +7,6 @@ import org.wu.framework.lazy.orm.core.stereotype.LazyScan;
"org.framework.lazy.cloud.network.heartbeat.dns.standalone.infrastructure.entity",
"org.framework.lazy.cloud.network.heartbeat.dns.cluster.infrastructure.entity"
})
-@ComponentScan(basePackages = "org.framework.lazy.cloud.network.heartbeat.dns")
+@ComponentScan(basePackages = "org.framework.lazy.cloud.network.heartbeat.protocol")
public class EnableDnsAutoConfiguration {
}
diff --git a/wu-lazy-cloud-heartbeat-dns/src/main/java/org/framework/lazy/cloud/network/heartbeat/dns/config/DnsAutoConfiguration.java b/wu-lazy-cloud-heartbeat-dns/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/config/DnsAutoConfiguration.java
similarity index 74%
rename from wu-lazy-cloud-heartbeat-dns/src/main/java/org/framework/lazy/cloud/network/heartbeat/dns/config/DnsAutoConfiguration.java
rename to wu-lazy-cloud-heartbeat-dns/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/config/DnsAutoConfiguration.java
index dc850f3..6659e63 100644
--- a/wu-lazy-cloud-heartbeat-dns/src/main/java/org/framework/lazy/cloud/network/heartbeat/dns/config/DnsAutoConfiguration.java
+++ b/wu-lazy-cloud-heartbeat-dns/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/config/DnsAutoConfiguration.java
@@ -1,4 +1,4 @@
-package org.framework.lazy.cloud.network.heartbeat.dns.config;
+package org.framework.lazy.cloud.network.heartbeat.protocol.config;
import org.springframework.beans.factory.config.BeanDefinition;
diff --git a/wu-lazy-cloud-heartbeat-dns/src/main/java/org/framework/lazy/cloud/network/heartbeat/dns/config/DnsFlowConfiguration.java b/wu-lazy-cloud-heartbeat-dns/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/config/DnsFlowConfiguration.java
similarity index 86%
rename from wu-lazy-cloud-heartbeat-dns/src/main/java/org/framework/lazy/cloud/network/heartbeat/dns/config/DnsFlowConfiguration.java
rename to wu-lazy-cloud-heartbeat-dns/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/config/DnsFlowConfiguration.java
index be33aa7..6cd63c1 100644
--- a/wu-lazy-cloud-heartbeat-dns/src/main/java/org/framework/lazy/cloud/network/heartbeat/dns/config/DnsFlowConfiguration.java
+++ b/wu-lazy-cloud-heartbeat-dns/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/config/DnsFlowConfiguration.java
@@ -1,4 +1,4 @@
-package org.framework.lazy.cloud.network.heartbeat.dns.config;
+package org.framework.lazy.cloud.network.heartbeat.protocol.config;
import org.framework.lazy.cloud.network.heartbeat.common.adapter.ChannelFlowAdapter;
import org.framework.lazy.cloud.network.heartbeat.common.advanced.flow.HandleChannelFlowAdvanced;
diff --git a/wu-lazy-cloud-heartbeat-dns/src/main/java/org/framework/lazy/cloud/network/heartbeat/dns/context/NettyDnsSocketApplicationListener.java b/wu-lazy-cloud-heartbeat-dns/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/context/NettyDnsSocketApplicationListener.java
similarity index 87%
rename from wu-lazy-cloud-heartbeat-dns/src/main/java/org/framework/lazy/cloud/network/heartbeat/dns/context/NettyDnsSocketApplicationListener.java
rename to wu-lazy-cloud-heartbeat-dns/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/context/NettyDnsSocketApplicationListener.java
index 8fcaa55..946dba7 100644
--- a/wu-lazy-cloud-heartbeat-dns/src/main/java/org/framework/lazy/cloud/network/heartbeat/dns/context/NettyDnsSocketApplicationListener.java
+++ b/wu-lazy-cloud-heartbeat-dns/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/context/NettyDnsSocketApplicationListener.java
@@ -1,4 +1,4 @@
-package org.framework.lazy.cloud.network.heartbeat.dns.context;
+package org.framework.lazy.cloud.network.heartbeat.protocol.context;
import lombok.extern.slf4j.Slf4j;
import org.framework.lazy.cloud.network.heartbeat.common.context.SocketApplicationListener;
diff --git a/wu-lazy-cloud-heartbeat-dns/src/main/java/org/framework/lazy/cloud/network/heartbeat/dns/init/InitDnsSocket.java b/wu-lazy-cloud-heartbeat-dns/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/init/InitDnsSocket.java
similarity index 67%
rename from wu-lazy-cloud-heartbeat-dns/src/main/java/org/framework/lazy/cloud/network/heartbeat/dns/init/InitDnsSocket.java
rename to wu-lazy-cloud-heartbeat-dns/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/init/InitDnsSocket.java
index 0ee461b..557e475 100644
--- a/wu-lazy-cloud-heartbeat-dns/src/main/java/org/framework/lazy/cloud/network/heartbeat/dns/init/InitDnsSocket.java
+++ b/wu-lazy-cloud-heartbeat-dns/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/init/InitDnsSocket.java
@@ -1,8 +1,8 @@
-package org.framework.lazy.cloud.network.heartbeat.dns.init;
+package org.framework.lazy.cloud.network.heartbeat.protocol.init;
import lombok.extern.slf4j.Slf4j;
-import org.framework.lazy.cloud.network.heartbeat.dns.context.NettyDnsSocketApplicationListener;
+import org.framework.lazy.cloud.network.heartbeat.protocol.context.NettyDnsSocketApplicationListener;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
diff --git a/wu-lazy-cloud-heartbeat-dns/src/main/resources/META-INF/spring.factories b/wu-lazy-cloud-heartbeat-dns/src/main/resources/META-INF/spring.factories
index f9b6772..f240753 100644
--- a/wu-lazy-cloud-heartbeat-dns/src/main/resources/META-INF/spring.factories
+++ b/wu-lazy-cloud-heartbeat-dns/src/main/resources/META-INF/spring.factories
@@ -1,6 +1,6 @@
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
-org.framework.lazy.cloud.network.heartbeat.dns.EnableDnsAutoConfiguration,\
-org.framework.lazy.cloud.network.heartbeat.dns.config.DnsAutoConfiguration,\
-org.framework.lazy.cloud.network.heartbeat.dns.init.InitDnsSocket,\
-org.framework.lazy.cloud.network.heartbeat.dns.config.DnsFlowConfiguration
+org.framework.lazy.cloud.network.heartbeat.protocol.EnableProtocolProxyAutoConfiguration,\
+org.framework.lazy.cloud.network.heartbeat.protocol.config.ProtocolProxyAutoConfiguration,\
+org.framework.lazy.cloud.network.heartbeat.protocol.init.InitProtocolProxySocket,\
+org.framework.lazy.cloud.network.heartbeat.protocol.config.ProtocolProxyFlowConfiguration
diff --git a/wu-lazy-cloud-heartbeat-dns/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/wu-lazy-cloud-heartbeat-dns/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
index 70b5eaa..dab0105 100644
--- a/wu-lazy-cloud-heartbeat-dns/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
+++ b/wu-lazy-cloud-heartbeat-dns/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -1,4 +1,4 @@
-org.framework.lazy.cloud.network.heartbeat.dns.EnableDnsAutoConfiguration
-org.framework.lazy.cloud.network.heartbeat.dns.config.DnsAutoConfiguration
-org.framework.lazy.cloud.network.heartbeat.dns.init.InitDnsSocket
-org.framework.lazy.cloud.network.heartbeat.dns.config.DnsFlowConfiguration
+org.framework.lazy.cloud.network.heartbeat.protocol.EnableProtocolProxyAutoConfiguration
+org.framework.lazy.cloud.network.heartbeat.protocol.config.ProtocolProxyAutoConfiguration
+org.framework.lazy.cloud.network.heartbeat.protocol.init.InitProtocolProxySocket
+org.framework.lazy.cloud.network.heartbeat.protocol.config.ProtocolProxyFlowConfiguration
diff --git a/wu-lazy-cloud-heartbeat-dns/src/test/java/org/framework/lazy/cloud/network/heartbeat/dns/DnsServer.java b/wu-lazy-cloud-heartbeat-dns/src/test/java/org/framework/lazy/cloud/network/heartbeat/protocol/DnsServer.java
similarity index 96%
rename from wu-lazy-cloud-heartbeat-dns/src/test/java/org/framework/lazy/cloud/network/heartbeat/dns/DnsServer.java
rename to wu-lazy-cloud-heartbeat-dns/src/test/java/org/framework/lazy/cloud/network/heartbeat/protocol/DnsServer.java
index 3e69d6c..cca97f3 100644
--- a/wu-lazy-cloud-heartbeat-dns/src/test/java/org/framework/lazy/cloud/network/heartbeat/dns/DnsServer.java
+++ b/wu-lazy-cloud-heartbeat-dns/src/test/java/org/framework/lazy/cloud/network/heartbeat/protocol/DnsServer.java
@@ -1,4 +1,4 @@
-package org.framework.lazy.cloud.network.heartbeat.dns;
+package org.framework.lazy.cloud.network.heartbeat.protocol;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
diff --git a/wu-lazy-cloud-heartbeat-dns/src/test/java/org/framework/lazy/cloud/network/heartbeat/dns/DnsServerHandler.java b/wu-lazy-cloud-heartbeat-dns/src/test/java/org/framework/lazy/cloud/network/heartbeat/protocol/DnsServerHandler.java
similarity index 95%
rename from wu-lazy-cloud-heartbeat-dns/src/test/java/org/framework/lazy/cloud/network/heartbeat/dns/DnsServerHandler.java
rename to wu-lazy-cloud-heartbeat-dns/src/test/java/org/framework/lazy/cloud/network/heartbeat/protocol/DnsServerHandler.java
index 18bb9d3..a8b1178 100644
--- a/wu-lazy-cloud-heartbeat-dns/src/test/java/org/framework/lazy/cloud/network/heartbeat/dns/DnsServerHandler.java
+++ b/wu-lazy-cloud-heartbeat-dns/src/test/java/org/framework/lazy/cloud/network/heartbeat/protocol/DnsServerHandler.java
@@ -1,4 +1,4 @@
-package org.framework.lazy.cloud.network.heartbeat.dns;
+package org.framework.lazy.cloud.network.heartbeat.protocol;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
@@ -62,6 +62,7 @@ public class DnsServerHandler extends SimpleChannelInboundHandler() {
+ @Override
+ public void initChannel(SocketChannel ch) throws Exception {
+ ch.pipeline().addLast(
+ new HttpClientCodec(),
+ new HttpObjectAggregator(1048576),
+ new HttpProxyServerHandler()
+ );
+ }
+ });
+
+ ChannelFuture f = b.bind(PORT).sync();
+ System.out.println("Proxy server started and listening on port " + PORT);
+ f.channel().closeFuture().sync();
+ } finally {
+ bossGroup.shutdownGracefully();
+ workerGroup.shutdownGracefully();
+ }
+ }
+
+ private static class HttpProxyServerHandler extends ChannelInboundHandlerAdapter {
+
+ private Channel outboundChannel;
+
+ @Override
+ public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception {
+ if (msg instanceof FullHttpRequest) {
+ FullHttpRequest request = (FullHttpRequest) msg;
+ URI uri = new URI(request.uri());
+ String host = uri.getHost();
+ int port = uri.getPort();
+ if (port == -1) {
+ port = 80;
+ }
+
+ Bootstrap b = new Bootstrap();
+ b.group(ctx.channel().eventLoop())
+ .channel(NioSocketChannel.class)
+ .handler(new ChannelInitializer() {
+ @Override
+ public void initChannel(SocketChannel ch) throws Exception {
+ ch.pipeline().addLast(
+ new HttpClientCodec(),
+ new HttpObjectAggregator(1048576),
+ new ProxyBackendHandler(ctx.channel())
+ );
+ }
+ });
+
+ ChannelFuture f = b.connect(host, port);
+ outboundChannel = f.channel();
+ f.addListener(new ChannelFutureListener() {
+ @Override
+ public void operationComplete(ChannelFuture future) throws Exception {
+ if (future.isSuccess()) {
+ // 连接成功,将客户端请求转发到目标服务器
+ outboundChannel.writeAndFlush(request.retain());
+ } else {
+ // 连接失败,关闭客户端通道
+ ctx.channel().close();
+ }
+ }
+ });
+ }
+ }
+
+ @Override
+ public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+ if (outboundChannel != null) {
+ closeOnFlush(outboundChannel);
+ }
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+ cause.printStackTrace();
+ closeOnFlush(ctx.channel());
+ }
+
+ static void closeOnFlush(Channel ch) {
+ if (ch.isActive()) {
+ ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
+ }
+ }
+ }
+
+ private static class ProxyBackendHandler extends ChannelInboundHandlerAdapter {
+
+ private final Channel inboundChannel;
+
+ ProxyBackendHandler(Channel inboundChannel) {
+ this.inboundChannel = inboundChannel;
+ }
+
+ @Override
+ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+ // 将目标服务器的响应转发给客户端
+ inboundChannel.writeAndFlush(msg);
+ }
+
+ @Override
+ public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+ closeOnFlush(inboundChannel);
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+ cause.printStackTrace();
+ closeOnFlush(ctx.channel());
+ }
+
+ static void closeOnFlush(Channel ch) {
+ if (ch.isActive()) {
+ ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/wu-lazy-cloud-heartbeat-dns/src/test/java/org/framework/lazy/cloud/network/heartbeat/protocol/MultiProtocolProxyServer.java b/wu-lazy-cloud-heartbeat-dns/src/test/java/org/framework/lazy/cloud/network/heartbeat/protocol/MultiProtocolProxyServer.java
new file mode 100644
index 0000000..4a156e5
--- /dev/null
+++ b/wu-lazy-cloud-heartbeat-dns/src/test/java/org/framework/lazy/cloud/network/heartbeat/protocol/MultiProtocolProxyServer.java
@@ -0,0 +1,312 @@
+package org.framework.lazy.cloud.network.heartbeat.protocol;
+
+import io.netty.bootstrap.Bootstrap;
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.*;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.DatagramPacket;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioDatagramChannel;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import io.netty.channel.socket.nio.NioSocketChannel;
+import io.netty.handler.codec.http.*;
+import io.netty.handler.logging.LogLevel;
+import io.netty.handler.logging.LoggingHandler;
+
+import java.net.InetSocketAddress;
+import java.net.URI;
+
+public class MultiProtocolProxyServer {
+ private static final int HTTP_PORT = 8080;
+ private static final int TCP_PORT = 9090;
+ private static final int UDP_PORT = 10000;
+
+ public static void main(String[] args) throws Exception {
+ EventLoopGroup bossGroup = new NioEventLoopGroup();
+ EventLoopGroup workerGroup = new NioEventLoopGroup();
+
+ try {
+ // 启动 HTTP 代理服务器
+ startHttpProxy(bossGroup, workerGroup);
+ // 启动 TCP 代理服务器
+ startTcpProxy(bossGroup, workerGroup);
+ // 启动 UDP 代理服务器
+ startUdpProxy(workerGroup);
+
+ System.out.println("Proxy servers started.");
+ } finally {
+ bossGroup.shutdownGracefully();
+ workerGroup.shutdownGracefully();
+ }
+ }
+
+ private static void startHttpProxy(EventLoopGroup bossGroup, EventLoopGroup workerGroup) throws InterruptedException {
+ ServerBootstrap b = new ServerBootstrap();
+ b.group(bossGroup, workerGroup)
+ .channel(NioServerSocketChannel.class)
+ .handler(new LoggingHandler(LogLevel.INFO))
+ .childHandler(new ChannelInitializer() {
+ @Override
+ protected void initChannel(SocketChannel ch) throws Exception {
+ ch.pipeline().addLast(
+ new HttpClientCodec(),
+ new HttpObjectAggregator(1048576),
+ new HttpProxyServerHandler()
+ );
+ }
+ });
+
+ b.bind(HTTP_PORT).sync();
+ System.out.println("HTTP proxy server started on port " + HTTP_PORT);
+ }
+
+ private static void startTcpProxy(EventLoopGroup bossGroup, EventLoopGroup workerGroup) throws InterruptedException {
+ ServerBootstrap b = new ServerBootstrap();
+ b.group(bossGroup, workerGroup)
+ .channel(NioServerSocketChannel.class)
+ .handler(new LoggingHandler(LogLevel.INFO))
+ .childHandler(new ChannelInitializer() {
+ @Override
+ protected void initChannel(SocketChannel ch) throws Exception {
+ ch.pipeline().addLast(new TcpProxyServerHandler());
+ }
+ });
+
+ b.bind(TCP_PORT).sync();
+ System.out.println("TCP proxy server started on port " + TCP_PORT);
+ }
+
+ private static void startUdpProxy(EventLoopGroup workerGroup) throws InterruptedException {
+ Bootstrap b = new Bootstrap();
+ b.group(workerGroup)
+ .channel(NioDatagramChannel.class)
+ .handler(new UdpProxyServerHandler());
+
+ b.bind(UDP_PORT).sync();
+ System.out.println("UDP proxy server started on port " + UDP_PORT);
+ }
+
+ private static class HttpProxyServerHandler extends ChannelInboundHandlerAdapter {
+ private Channel outboundChannel;
+
+ @Override
+ public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception {
+ if (msg instanceof FullHttpRequest) {
+ FullHttpRequest request = (FullHttpRequest) msg;
+ URI uri = new URI(request.uri());
+ String host = uri.getHost();
+ int port = uri.getPort();
+ if (port == -1) {
+ port = 80;
+ }
+
+ Bootstrap b = new Bootstrap();
+ b.group(ctx.channel().eventLoop())
+ .channel(NioSocketChannel.class)
+ .handler(new ChannelInitializer() {
+ @Override
+ protected void initChannel(SocketChannel ch) throws Exception {
+ ch.pipeline().addLast(
+ new HttpClientCodec(),
+ new HttpObjectAggregator(1048576),
+ new HttpProxyBackendHandler(ctx.channel())
+ );
+ }
+ });
+
+ ChannelFuture f = b.connect(host, port);
+ outboundChannel = f.channel();
+ f.addListener(new ChannelFutureListener() {
+ @Override
+ public void operationComplete(ChannelFuture future) throws Exception {
+ if (future.isSuccess()) {
+ outboundChannel.writeAndFlush(request.retain());
+ } else {
+ ctx.channel().close();
+ }
+ }
+ });
+ }
+ }
+
+ @Override
+ public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+ if (outboundChannel != null) {
+ closeOnFlush(outboundChannel);
+ }
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+ cause.printStackTrace();
+ closeOnFlush(ctx.channel());
+ }
+
+ static void closeOnFlush(Channel ch) {
+ if (ch.isActive()) {
+ ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
+ }
+ }
+ }
+
+ private static class HttpProxyBackendHandler extends ChannelInboundHandlerAdapter {
+ private final Channel inboundChannel;
+
+ HttpProxyBackendHandler(Channel inboundChannel) {
+ this.inboundChannel = inboundChannel;
+ }
+
+ @Override
+ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+ inboundChannel.writeAndFlush(msg);
+ }
+
+ @Override
+ public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+ closeOnFlush(inboundChannel);
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+ cause.printStackTrace();
+ closeOnFlush(ctx.channel());
+ }
+
+ static void closeOnFlush(Channel ch) {
+ if (ch.isActive()) {
+ ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
+ }
+ }
+ }
+
+ private static class TcpProxyServerHandler extends ChannelInboundHandlerAdapter {
+ private Channel outboundChannel;
+
+ @Override
+ public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception {
+ if (outboundChannel == null) {
+ // 假设目标服务器地址和端口
+ String targetHost = "example.com";
+ int targetPort = 80;
+
+ Bootstrap b = new Bootstrap();
+ b.group(ctx.channel().eventLoop())
+ .channel(NioSocketChannel.class)
+ .handler(new ChannelInitializer() {
+ @Override
+ protected void initChannel(SocketChannel ch) throws Exception {
+ ch.pipeline().addLast(new TcpProxyBackendHandler(ctx.channel()));
+ }
+ });
+
+ ChannelFuture f = b.connect(targetHost, targetPort);
+ outboundChannel = f.channel();
+ f.addListener(new ChannelFutureListener() {
+ @Override
+ public void operationComplete(ChannelFuture future) throws Exception {
+ if (future.isSuccess()) {
+ outboundChannel.writeAndFlush(msg);
+ } else {
+ ctx.channel().close();
+ }
+ }
+ });
+ } else {
+ outboundChannel.writeAndFlush(msg);
+ }
+ }
+
+ @Override
+ public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+ if (outboundChannel != null) {
+ closeOnFlush(outboundChannel);
+ }
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+ cause.printStackTrace();
+ closeOnFlush(ctx.channel());
+ }
+
+ static void closeOnFlush(Channel ch) {
+ if (ch.isActive()) {
+ ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
+ }
+ }
+ }
+
+ private static class TcpProxyBackendHandler extends ChannelInboundHandlerAdapter {
+ private final Channel inboundChannel;
+
+ TcpProxyBackendHandler(Channel inboundChannel) {
+ this.inboundChannel = inboundChannel;
+ }
+
+ @Override
+ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+ inboundChannel.writeAndFlush(msg);
+ }
+
+ @Override
+ public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+ closeOnFlush(inboundChannel);
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+ cause.printStackTrace();
+ closeOnFlush(ctx.channel());
+ }
+
+ static void closeOnFlush(Channel ch) {
+ if (ch.isActive()) {
+ ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
+ }
+ }
+ }
+
+ private static class UdpProxyServerHandler extends SimpleChannelInboundHandler {
+ private InetSocketAddress targetAddress = new InetSocketAddress("example.com", 80);
+
+ @Override
+ protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception {
+ Bootstrap b = new Bootstrap();
+ b.group(ctx.channel().eventLoop())
+ .channel(NioDatagramChannel.class)
+ .handler(new UdpProxyBackendHandler(ctx.channel(), packet.sender()));
+
+ Channel channel = b.bind(0).sync().channel();
+ channel.writeAndFlush(new DatagramPacket(packet.content().retain(), targetAddress));
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+ cause.printStackTrace();
+ ctx.close();
+ }
+ }
+
+ private static class UdpProxyBackendHandler extends SimpleChannelInboundHandler {
+ private final Channel inboundChannel;
+ private final InetSocketAddress clientAddress;
+
+ UdpProxyBackendHandler(Channel inboundChannel, InetSocketAddress clientAddress) {
+ this.inboundChannel = inboundChannel;
+ this.clientAddress = clientAddress;
+ }
+
+ @Override
+ protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception {
+ inboundChannel.writeAndFlush(new DatagramPacket(packet.content().retain(), clientAddress));
+ ctx.channel().close();
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+ cause.printStackTrace();
+ ctx.close();
+ }
+ }
+}
\ No newline at end of file
diff --git a/wu-lazy-cloud-heartbeat-dns/src/test/java/org/framework/lazy/cloud/network/heartbeat/dns/demo/DnsServer.java b/wu-lazy-cloud-heartbeat-dns/src/test/java/org/framework/lazy/cloud/network/heartbeat/protocol/demo/DnsServer.java
similarity index 81%
rename from wu-lazy-cloud-heartbeat-dns/src/test/java/org/framework/lazy/cloud/network/heartbeat/dns/demo/DnsServer.java
rename to wu-lazy-cloud-heartbeat-dns/src/test/java/org/framework/lazy/cloud/network/heartbeat/protocol/demo/DnsServer.java
index 055cf4e..292cef7 100644
--- a/wu-lazy-cloud-heartbeat-dns/src/test/java/org/framework/lazy/cloud/network/heartbeat/dns/demo/DnsServer.java
+++ b/wu-lazy-cloud-heartbeat-dns/src/test/java/org/framework/lazy/cloud/network/heartbeat/protocol/demo/DnsServer.java
@@ -1,13 +1,11 @@
-package org.framework.lazy.cloud.network.heartbeat.dns.demo;
+package org.framework.lazy.cloud.network.heartbeat.protocol.demo;
import com.github.xiaoymin.knife4j.core.util.StrUtil;
import io.netty.bootstrap.Bootstrap;
-import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
-import io.netty.channel.socket.DatagramChannel;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.handler.codec.dns.*;
import io.netty.util.AttributeKey;
@@ -16,6 +14,9 @@ import lombok.extern.slf4j.Slf4j;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
@@ -75,10 +76,25 @@ public final class DnsServer {
DatagramDnsResponse dnsResponse = new DatagramDnsResponse(msg.recipient(), msg.sender(), id);
dnsResponse.addRecord(DnsSection.QUESTION, question);
+ InetAddress inetAddress = null;
+ try {
+ inetAddress = Inet4Address.getByName("www.baidu.com");
+ } catch (UnknownHostException e) {
+ throw new RuntimeException(e);
+ }
+ byte[] ipBytes = inetAddress.getAddress();
// just print the IP after query
+// DefaultDnsRawRecord queryAnswer = new DefaultDnsRawRecord(
+// question.name(),
+// DnsRecordType.A, 600, Unpooled.wrappedBuffer(new byte[]{(byte) 192, (byte) 168, 1, 1}));
+
DefaultDnsRawRecord queryAnswer = new DefaultDnsRawRecord(
question.name(),
- DnsRecordType.A, 600, Unpooled.wrappedBuffer(new byte[]{(byte) 192, (byte) 168, 1, 1}));
+ DnsRecordType.A,
+ 600,
+ Unpooled.wrappedBuffer(ipBytes)
+ );
+
dnsResponse.addRecord(DnsSection.ANSWER, queryAnswer);
return dnsResponse;
}
diff --git a/wu-lazy-cloud-heartbeat-dns/src/test/java/org/framework/lazy/cloud/network/heartbeat/dns/demo/ProxyUdp.java b/wu-lazy-cloud-heartbeat-dns/src/test/java/org/framework/lazy/cloud/network/heartbeat/protocol/demo/ProxyUdp.java
similarity index 98%
rename from wu-lazy-cloud-heartbeat-dns/src/test/java/org/framework/lazy/cloud/network/heartbeat/dns/demo/ProxyUdp.java
rename to wu-lazy-cloud-heartbeat-dns/src/test/java/org/framework/lazy/cloud/network/heartbeat/protocol/demo/ProxyUdp.java
index ac4f377..5850dc5 100644
--- a/wu-lazy-cloud-heartbeat-dns/src/test/java/org/framework/lazy/cloud/network/heartbeat/dns/demo/ProxyUdp.java
+++ b/wu-lazy-cloud-heartbeat-dns/src/test/java/org/framework/lazy/cloud/network/heartbeat/protocol/demo/ProxyUdp.java
@@ -1,4 +1,4 @@
-package org.framework.lazy.cloud.network.heartbeat.dns.demo;
+package org.framework.lazy.cloud.network.heartbeat.protocol.demo;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBufUtil;
diff --git a/wu-lazy-cloud-heartbeat-protocol-proxy/pom.xml b/wu-lazy-cloud-heartbeat-protocol-proxy/pom.xml
new file mode 100644
index 0000000..429533c
--- /dev/null
+++ b/wu-lazy-cloud-heartbeat-protocol-proxy/pom.xml
@@ -0,0 +1,59 @@
+
+
+
+ top.wu2020
+ wu-lazy-cloud-network
+ 1.3.0-JDK17-SNAPSHOT
+
+
+ 4.0.0
+
+ wu-lazy-cloud-heartbeat-protocol-proxy
+ 云上心跳服务协议代理
+
+
+ 17
+ 17
+
+
+
+
+ top.wu2020
+ wu-framework-web
+
+
+
+ top.wu2020
+ wu-lazy-cloud-heartbeat-common
+
+
+
+ mysql
+ mysql-connector-java
+ 8.0.33
+
+
+
+ top.wu2020
+ wu-database-lazy-plus-starter
+
+
+ com.alibaba
+ fastjson
+ 2.0.50
+
+
+ io.swagger.core.v3
+ swagger-annotations-jakarta
+ 2.2.21
+
+
+ top.wu2020
+ wu-framework-lazy-orm-spring-starter
+
+
+
+
+
\ No newline at end of file
diff --git a/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/EnableProtocolProxyAutoConfiguration.java b/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/EnableProtocolProxyAutoConfiguration.java
new file mode 100644
index 0000000..04ae23b
--- /dev/null
+++ b/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/EnableProtocolProxyAutoConfiguration.java
@@ -0,0 +1,11 @@
+package org.framework.lazy.cloud.network.heartbeat.protocol;
+
+import org.springframework.context.annotation.ComponentScan;
+import org.wu.framework.lazy.orm.core.stereotype.LazyScan;
+
+@LazyScan(scanBasePackages = {
+ "org.framework.lazy.cloud.network.heartbeat.dns.standalone.infrastructure.entity"
+})
+@ComponentScan(basePackages = "org.framework.lazy.cloud.network.heartbeat.protocol")
+public class EnableProtocolProxyAutoConfiguration {
+}
diff --git a/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/config/ProtocolProxyAutoConfiguration.java b/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/config/ProtocolProxyAutoConfiguration.java
new file mode 100644
index 0000000..d541dbd
--- /dev/null
+++ b/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/config/ProtocolProxyAutoConfiguration.java
@@ -0,0 +1,11 @@
+package org.framework.lazy.cloud.network.heartbeat.protocol.config;
+
+
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.context.annotation.Role;
+
+
+@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
+public class ProtocolProxyAutoConfiguration {
+
+}
diff --git a/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/config/ProtocolProxyFlowConfiguration.java b/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/config/ProtocolProxyFlowConfiguration.java
new file mode 100644
index 0000000..7607755
--- /dev/null
+++ b/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/config/ProtocolProxyFlowConfiguration.java
@@ -0,0 +1,15 @@
+package org.framework.lazy.cloud.network.heartbeat.protocol.config;
+
+import org.framework.lazy.cloud.network.heartbeat.common.adapter.ChannelFlowAdapter;
+import org.framework.lazy.cloud.network.heartbeat.common.advanced.flow.HandleChannelFlowAdvanced;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.context.annotation.Role;
+
+/**
+ * @see ChannelFlowAdapter
+ * @see HandleChannelFlowAdvanced
+ */
+@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
+public class ProtocolProxyFlowConfiguration {
+
+}
diff --git a/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/context/NettyHttpProxySocketApplicationListener.java b/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/context/NettyHttpProxySocketApplicationListener.java
new file mode 100644
index 0000000..ddc3de9
--- /dev/null
+++ b/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/context/NettyHttpProxySocketApplicationListener.java
@@ -0,0 +1,85 @@
+package org.framework.lazy.cloud.network.heartbeat.protocol.context;
+
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.*;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import jakarta.annotation.PreDestroy;
+import lombok.extern.slf4j.Slf4j;
+import org.framework.lazy.cloud.network.heartbeat.common.context.SocketApplicationListener;
+import org.framework.lazy.cloud.network.heartbeat.protocol.filter.NettyHttpProxyFilter;
+import org.framework.lazy.cloud.network.heartbeat.protocol.properties.ProtocolProxyProperties;
+import org.springframework.stereotype.Component;
+
+@Slf4j
+@Component
+public class NettyHttpProxySocketApplicationListener implements SocketApplicationListener {
+
+ private final EventLoopGroup bossGroup = new NioEventLoopGroup();
+ private final EventLoopGroup workerGroup = new NioEventLoopGroup();
+ private ChannelFuture channelFuture;
+ private final NettyHttpProxyFilter nettyHttpProxyFilter;// 通道业务处理
+ private final ProtocolProxyProperties protocolProxyProperties;
+
+ public NettyHttpProxySocketApplicationListener(NettyHttpProxyFilter nettyHttpProxyFilter, ProtocolProxyProperties protocolProxyProperties) {
+ this.nettyHttpProxyFilter = nettyHttpProxyFilter;
+ this.protocolProxyProperties = protocolProxyProperties;
+ }
+
+
+ /**
+ * 运行
+ *
+ * @throws InterruptedException
+ */
+ @Override
+ public void doRunning() throws Exception {
+ try {
+ ProtocolProxyProperties.HttpProtocolProxy httpProtocolProxy = protocolProxyProperties.getHttpProtocolProxy();
+ Integer httpProtocolProxyPort = httpProtocolProxy.getPort();
+ ServerBootstrap b = new ServerBootstrap();
+ b.group(bossGroup, workerGroup)
+ .channel(NioServerSocketChannel.class)
+ // 给服务端channel设置属性
+ // 设置读缓冲区为2M
+ .childOption(ChannelOption.SO_RCVBUF, 2048 * 1024)
+ // 设置写缓冲区为1M
+ .childOption(ChannelOption.SO_SNDBUF, 1024 * 1024)
+
+ .childOption(ChannelOption.SO_KEEPALIVE, true)
+// .childOption(ChannelOption.TCP_NODELAY, false)
+ .childOption(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000 * 60)//连接超时时间设置为 60 秒
+// .childOption(ChannelOption.RCVBUF_ALLOCATOR, new NettyRecvByteBufAllocator(1024 * 1024))//用于Channel分配接受Buffer的分配器 默认AdaptiveRecvByteBufAllocator.DEFAULT
+ .childOption(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(1024 * 1024, 1024 * 1024 * 2))
+
+ .childHandler(nettyHttpProxyFilter);
+ channelFuture = b.bind(httpProtocolProxyPort).sync();
+
+ channelFuture.addListener((ChannelFutureListener) channelFuture -> {
+ // 服务器已启动
+ log.info("http 协议代理 服务器启动成功");
+ });
+ channelFuture.channel().closeFuture().sync();
+ } catch (Exception e) {
+ log.error("启动http 协议代理 失败", e);
+ } finally {
+ destroy();
+ // 服务器已关闭
+ log.warn("http 协议代理 服务关闭");
+ }
+ }
+
+ @PreDestroy
+ @Override
+ public void destroy() throws Exception {
+ if (channelFuture != null) {
+ channelFuture.channel().close().syncUninterruptibly();
+ }
+ if (!bossGroup.isShutdown()) {
+ bossGroup.shutdownGracefully();
+ }
+ if (!workerGroup.isShutdown()) {
+ workerGroup.shutdownGracefully();
+ }
+ }
+}
\ No newline at end of file
diff --git a/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/context/NettyTcpProxySocketApplicationListener.java b/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/context/NettyTcpProxySocketApplicationListener.java
new file mode 100644
index 0000000..d4ea289
--- /dev/null
+++ b/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/context/NettyTcpProxySocketApplicationListener.java
@@ -0,0 +1,86 @@
+package org.framework.lazy.cloud.network.heartbeat.protocol.context;
+
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.*;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import jakarta.annotation.PreDestroy;
+import lombok.extern.slf4j.Slf4j;
+import org.framework.lazy.cloud.network.heartbeat.common.context.SocketApplicationListener;
+import org.framework.lazy.cloud.network.heartbeat.protocol.filter.NettyHttpProxyFilter;
+import org.framework.lazy.cloud.network.heartbeat.protocol.filter.NettyTcpProxyFilter;
+import org.framework.lazy.cloud.network.heartbeat.protocol.properties.ProtocolProxyProperties;
+import org.springframework.stereotype.Component;
+
+@Slf4j
+@Component
+public class NettyTcpProxySocketApplicationListener implements SocketApplicationListener {
+
+ private final EventLoopGroup bossGroup = new NioEventLoopGroup();
+ private final EventLoopGroup workerGroup = new NioEventLoopGroup();
+ private final NettyTcpProxyFilter nettyTcpProxyFilter;// 通道业务处理
+ private ChannelFuture channelFuture;
+ private final ProtocolProxyProperties protocolProxyProperties;
+
+ public NettyTcpProxySocketApplicationListener(NettyTcpProxyFilter nettyTcpProxyFilter, ProtocolProxyProperties protocolProxyProperties) {
+ this.nettyTcpProxyFilter = nettyTcpProxyFilter;
+ this.protocolProxyProperties = protocolProxyProperties;
+ }
+
+
+ /**
+ * 运行
+ *
+ * @throws InterruptedException
+ */
+ @Override
+ public void doRunning() throws Exception {
+ try {
+ ProtocolProxyProperties.TcpProtocolProxy tcpHttpProtocolProxy = protocolProxyProperties.getTcpHttpProtocolProxy();
+ Integer httpProtocolProxyPort = tcpHttpProtocolProxy.getPort();
+ ServerBootstrap b = new ServerBootstrap();
+ b.group(bossGroup, workerGroup)
+ .channel(NioServerSocketChannel.class)
+ // 给服务端channel设置属性
+ // 设置读缓冲区为2M
+ .childOption(ChannelOption.SO_RCVBUF, 2048 * 1024)
+ // 设置写缓冲区为1M
+ .childOption(ChannelOption.SO_SNDBUF, 1024 * 1024)
+
+ .childOption(ChannelOption.SO_KEEPALIVE, true)
+// .childOption(ChannelOption.TCP_NODELAY, false)
+ .childOption(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000 * 60)//连接超时时间设置为 60 秒
+// .childOption(ChannelOption.RCVBUF_ALLOCATOR, new NettyRecvByteBufAllocator(1024 * 1024))//用于Channel分配接受Buffer的分配器 默认AdaptiveRecvByteBufAllocator.DEFAULT
+ .childOption(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(1024 * 1024, 1024 * 1024 * 2))
+
+ .childHandler(nettyTcpProxyFilter);
+ channelFuture = b.bind(httpProtocolProxyPort).sync();
+
+ channelFuture.addListener((ChannelFutureListener) channelFuture -> {
+ // 服务器已启动
+ log.info("TCP 协议代理 服务器启动成功");
+ });
+ channelFuture.channel().closeFuture().sync();
+ } catch (Exception e) {
+ log.error("启动TCP 协议代理 失败", e);
+ } finally {
+ destroy();
+ // 服务器已关闭
+ log.warn("TCP 协议代理 服务关闭");
+ }
+ }
+
+ @PreDestroy
+ @Override
+ public void destroy() throws Exception {
+ if (channelFuture != null) {
+ channelFuture.channel().close().syncUninterruptibly();
+ }
+ if (!bossGroup.isShutdown()) {
+ bossGroup.shutdownGracefully();
+ }
+ if (!workerGroup.isShutdown()) {
+ workerGroup.shutdownGracefully();
+ }
+ }
+}
\ No newline at end of file
diff --git a/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/context/NettyUdpProxySocketApplicationListener.java b/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/context/NettyUdpProxySocketApplicationListener.java
new file mode 100644
index 0000000..b376b5b
--- /dev/null
+++ b/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/context/NettyUdpProxySocketApplicationListener.java
@@ -0,0 +1,89 @@
+package org.framework.lazy.cloud.network.heartbeat.protocol.context;
+
+import io.netty.bootstrap.Bootstrap;
+import io.netty.channel.*;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.nio.NioDatagramChannel;
+import jakarta.annotation.PreDestroy;
+import lombok.extern.slf4j.Slf4j;
+import org.framework.lazy.cloud.network.heartbeat.common.adapter.ChannelTypeAdapter;
+import org.framework.lazy.cloud.network.heartbeat.common.advanced.HandleChannelTypeAdvanced;
+import org.framework.lazy.cloud.network.heartbeat.common.context.SocketApplicationListener;
+import org.framework.lazy.cloud.network.heartbeat.protocol.handler.NettyUdpProxyHandler;
+import org.framework.lazy.cloud.network.heartbeat.protocol.properties.ProtocolProxyProperties;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+@Slf4j
+@Component
+public class NettyUdpProxySocketApplicationListener implements SocketApplicationListener {
+
+ private final EventLoopGroup bossGroup = new NioEventLoopGroup();
+ private ChannelFuture channelFuture;
+ private final ProtocolProxyProperties protocolProxyProperties;
+ private final List handleChannelTypeAdvancedList;
+
+ public NettyUdpProxySocketApplicationListener(ProtocolProxyProperties protocolProxyProperties,
+ List handleChannelTypeAdvancedList) {
+ this.protocolProxyProperties = protocolProxyProperties;
+ this.handleChannelTypeAdvancedList = handleChannelTypeAdvancedList;
+ }
+
+
+ /**
+ * 运行
+ *
+ * @throws InterruptedException
+ */
+ @Override
+ public void doRunning() throws Exception {
+ try {
+ ChannelTypeAdapter channelTypeAdapter = new ChannelTypeAdapter(handleChannelTypeAdvancedList);
+ NettyUdpProxyHandler nettyUdpProxyHandler = new NettyUdpProxyHandler(channelTypeAdapter);// 通道业务处理
+
+ ProtocolProxyProperties.UdpProtocolProxy udpHttpProtocolProxy = protocolProxyProperties.getUdpHttpProtocolProxy();
+ Integer udpProtocolProxyPort = udpHttpProtocolProxy.getPort();
+ Bootstrap b = new Bootstrap();
+ b.group(bossGroup)
+ .channel(NioDatagramChannel.class)
+ // 给服务端channel设置属性
+ // 设置读缓冲区为2M
+ .option(ChannelOption.SO_RCVBUF, 2048 * 1024)
+ // 设置写缓冲区为1M
+ .option(ChannelOption.SO_SNDBUF, 1024 * 1024)
+
+ .option(ChannelOption.SO_KEEPALIVE, true)
+// .childOption(ChannelOption.TCP_NODELAY, false)
+ .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000 * 60)//连接超时时间设置为 60 秒
+// .childOption(ChannelOption.RCVBUF_ALLOCATOR, new NettyRecvByteBufAllocator(1024 * 1024))//用于Channel分配接受Buffer的分配器 默认AdaptiveRecvByteBufAllocator.DEFAULT
+ .option(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(1024 * 1024, 1024 * 1024 * 2))
+
+ .handler(nettyUdpProxyHandler);
+ channelFuture = b.bind(udpProtocolProxyPort).sync();
+
+ channelFuture.addListener((ChannelFutureListener) channelFuture -> {
+ // 服务器已启动
+ log.info("UDP 协议代理 服务器启动成功");
+ });
+ channelFuture.channel().closeFuture().sync();
+ } catch (Exception e) {
+ log.error("启动UDP 协议代理 失败", e);
+ } finally {
+ destroy();
+ // 服务器已关闭
+ log.warn("UDP 协议代理 服务关闭");
+ }
+ }
+
+ @PreDestroy
+ @Override
+ public void destroy() throws Exception {
+ if (channelFuture != null) {
+ channelFuture.channel().close().syncUninterruptibly();
+ }
+ if (!bossGroup.isShutdown()) {
+ bossGroup.shutdownGracefully();
+ }
+ }
+}
\ No newline at end of file
diff --git a/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/filter/NettyHttpProxyFilter.java b/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/filter/NettyHttpProxyFilter.java
new file mode 100644
index 0000000..43bb33d
--- /dev/null
+++ b/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/filter/NettyHttpProxyFilter.java
@@ -0,0 +1,52 @@
+package org.framework.lazy.cloud.network.heartbeat.protocol.filter;
+
+import io.netty.channel.ChannelPipeline;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.handler.codec.http.HttpClientCodec;
+import io.netty.handler.codec.http.HttpObjectAggregator;
+import org.framework.lazy.cloud.network.heartbeat.common.adapter.ChannelTypeAdapter;
+import org.framework.lazy.cloud.network.heartbeat.common.advanced.HandleChannelTypeAdvanced;
+import org.framework.lazy.cloud.network.heartbeat.common.filter.DebugChannelInitializer;
+import org.framework.lazy.cloud.network.heartbeat.protocol.handler.NettyHttpProxyHandler;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * description
+ *
+ * @author 吴佳伟
+ * @date 2023/09/13 10:26
+ */
+@Component
+public class NettyHttpProxyFilter extends DebugChannelInitializer {
+ private final List handleChannelTypeAdvancedList;
+
+ public NettyHttpProxyFilter(List handleChannelTypeAdvancedList) {
+ this.handleChannelTypeAdvancedList = handleChannelTypeAdvancedList;
+ }
+
+ @Override
+ protected void initChannel0(SocketChannel ch) throws Exception {
+ ChannelPipeline pipeline = ch.pipeline();
+ // 以("\n")为结尾分割的 解码器
+ // 解码、编码
+// pipeline.addLast(new NettyProxyMsgDecoder(Integer.MAX_VALUE, 0, 4, -4, 0));
+// pipeline.addLast(new NettyProxyMsgEncoder());
+//// ph.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
+// // 解码和编码,应和客户端一致
+// //入参说明: 读超时时间、写超时时间、所有类型的超时时间、时间格式
+//
+// pipeline.addLast(new IdleStateHandler(5, 0, 0, TimeUnit.SECONDS));
+// pipeline.addLast("decoder", new StringDecoder());
+// pipeline.addLast("encoder", new StringEncoder());
+
+ // 类型处理器适配器
+ ChannelTypeAdapter channelTypeAdapter = new ChannelTypeAdapter(handleChannelTypeAdvancedList);
+ pipeline.addLast("doHandler", new NettyHttpProxyHandler(channelTypeAdapter));// 服务端业务逻辑
+ pipeline.addLast(new HttpClientCodec());
+ pipeline.addLast(new HttpObjectAggregator(1048576));
+
+
+ }
+}
\ No newline at end of file
diff --git a/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/filter/NettyTcpProxyFilter.java b/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/filter/NettyTcpProxyFilter.java
new file mode 100644
index 0000000..1374242
--- /dev/null
+++ b/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/filter/NettyTcpProxyFilter.java
@@ -0,0 +1,40 @@
+package org.framework.lazy.cloud.network.heartbeat.protocol.filter;
+
+import io.netty.channel.ChannelPipeline;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.handler.codec.http.HttpClientCodec;
+import io.netty.handler.codec.http.HttpObjectAggregator;
+import org.framework.lazy.cloud.network.heartbeat.common.adapter.ChannelTypeAdapter;
+import org.framework.lazy.cloud.network.heartbeat.common.advanced.HandleChannelTypeAdvanced;
+import org.framework.lazy.cloud.network.heartbeat.common.filter.DebugChannelInitializer;
+import org.framework.lazy.cloud.network.heartbeat.protocol.handler.NettyHttpProxyHandler;
+import org.framework.lazy.cloud.network.heartbeat.protocol.handler.NettyTcpProxyHandler;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * description
+ *
+ * @author 吴佳伟
+ * @date 2023/09/13 10:26
+ */
+@Component
+public class NettyTcpProxyFilter extends DebugChannelInitializer {
+ private final List handleChannelTypeAdvancedList;
+
+ public NettyTcpProxyFilter(List handleChannelTypeAdvancedList) {
+ this.handleChannelTypeAdvancedList = handleChannelTypeAdvancedList;
+ }
+
+ @Override
+ protected void initChannel0(SocketChannel ch) throws Exception {
+ ChannelPipeline pipeline = ch.pipeline();
+ // 类型处理器适配器
+ ChannelTypeAdapter channelTypeAdapter = new ChannelTypeAdapter(handleChannelTypeAdvancedList);
+ pipeline.addLast("doHandler", new NettyTcpProxyHandler(channelTypeAdapter));// 服务端业务逻辑
+
+
+
+ }
+}
\ No newline at end of file
diff --git a/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/handler/NettyHttpProxyBackendHandler.java b/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/handler/NettyHttpProxyBackendHandler.java
new file mode 100644
index 0000000..26a63b1
--- /dev/null
+++ b/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/handler/NettyHttpProxyBackendHandler.java
@@ -0,0 +1,39 @@
+package org.framework.lazy.cloud.network.heartbeat.protocol.handler;
+
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFutureListener;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+
+
+
+public class NettyHttpProxyBackendHandler extends ChannelInboundHandlerAdapter {
+ private final Channel inboundChannel;
+
+ NettyHttpProxyBackendHandler(Channel inboundChannel) {
+ this.inboundChannel = inboundChannel;
+ }
+
+ @Override
+ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+ inboundChannel.writeAndFlush(msg);
+ }
+
+ @Override
+ public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+ closeOnFlush(inboundChannel);
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+ cause.printStackTrace();
+ closeOnFlush(ctx.channel());
+ }
+
+ static void closeOnFlush(Channel ch) {
+ if (ch.isActive()) {
+ ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
+ }
+ }
+}
\ No newline at end of file
diff --git a/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/handler/NettyHttpProxyHandler.java b/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/handler/NettyHttpProxyHandler.java
new file mode 100644
index 0000000..5cc71da
--- /dev/null
+++ b/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/handler/NettyHttpProxyHandler.java
@@ -0,0 +1,92 @@
+package org.framework.lazy.cloud.network.heartbeat.protocol.handler;
+
+import io.netty.bootstrap.Bootstrap;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.*;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioSocketChannel;
+import io.netty.handler.codec.http.FullHttpRequest;
+import io.netty.handler.codec.http.HttpClientCodec;
+import io.netty.handler.codec.http.HttpObjectAggregator;
+import lombok.extern.slf4j.Slf4j;
+import org.framework.lazy.cloud.network.heartbeat.common.adapter.ChannelTypeAdapter;
+
+import java.net.URI;
+
+/**
+ * description 服务端数据处理器
+ *
+ * @author 吴佳伟
+ * @date 2023/09/13 10:27
+ */
+@Slf4j
+public class NettyHttpProxyHandler extends ChannelInboundHandlerAdapter {
+
+ private final ChannelTypeAdapter channelTypeAdapter;
+ /**
+ * 传出数据延迟次数* 心跳时间作为关闭时间
+ */
+ private int transfer_count = 1;
+
+ public NettyHttpProxyHandler(ChannelTypeAdapter channelTypeAdapter) {
+ this.channelTypeAdapter = channelTypeAdapter;
+ }
+
+ private Channel outboundChannel;
+
+ @Override
+ public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception {
+ if (msg instanceof FullHttpRequest) {
+ FullHttpRequest request = (FullHttpRequest) msg;
+ URI uri = new URI(request.uri());
+ String host = uri.getHost();
+ int port = uri.getPort();
+ if (port == -1) {
+ port = 80;
+ }
+
+ Bootstrap b = new Bootstrap();
+ b.group(ctx.channel().eventLoop())
+ .channel(NioSocketChannel.class)
+ .handler(new ChannelInitializer() {
+ @Override
+ protected void initChannel(SocketChannel ch) throws Exception {
+ ch.pipeline().addLast(
+ new HttpClientCodec(),
+ new HttpObjectAggregator(1048576),
+ new NettyHttpProxyBackendHandler(ctx.channel())
+ );
+ }
+ });
+
+ ChannelFuture f = b.connect(host, port);
+ outboundChannel = f.channel();
+ f.addListener((ChannelFutureListener) future -> {
+ if (future.isSuccess()) {
+ outboundChannel.writeAndFlush(request.retain());
+ } else {
+ ctx.channel().close();
+ }
+ });
+ }
+ }
+
+ @Override
+ public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+ if (outboundChannel != null) {
+ closeOnFlush(outboundChannel);
+ }
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+ cause.printStackTrace();
+ closeOnFlush(ctx.channel());
+ }
+
+ static void closeOnFlush(Channel ch) {
+ if (ch.isActive()) {
+ ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
+ }
+ }
+}
\ No newline at end of file
diff --git a/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/handler/NettyTcpProxyBackendHandler.java b/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/handler/NettyTcpProxyBackendHandler.java
new file mode 100644
index 0000000..8ed76d0
--- /dev/null
+++ b/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/handler/NettyTcpProxyBackendHandler.java
@@ -0,0 +1,38 @@
+package org.framework.lazy.cloud.network.heartbeat.protocol.handler;
+
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFutureListener;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+
+
+public class NettyTcpProxyBackendHandler extends ChannelInboundHandlerAdapter {
+ private final Channel inboundChannel;
+
+ public NettyTcpProxyBackendHandler(Channel inboundChannel) {
+ this.inboundChannel = inboundChannel;
+ }
+
+ @Override
+ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+ inboundChannel.writeAndFlush(msg);
+ }
+
+ @Override
+ public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+ closeOnFlush(inboundChannel);
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+ cause.printStackTrace();
+ closeOnFlush(ctx.channel());
+ }
+
+ static void closeOnFlush(Channel ch) {
+ if (ch.isActive()) {
+ ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
+ }
+ }
+}
\ No newline at end of file
diff --git a/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/handler/NettyTcpProxyHandler.java b/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/handler/NettyTcpProxyHandler.java
new file mode 100644
index 0000000..2074868
--- /dev/null
+++ b/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/handler/NettyTcpProxyHandler.java
@@ -0,0 +1,84 @@
+package org.framework.lazy.cloud.network.heartbeat.protocol.handler;
+
+import io.netty.bootstrap.Bootstrap;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.*;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioSocketChannel;
+import io.netty.handler.codec.http.FullHttpRequest;
+import io.netty.handler.codec.http.HttpClientCodec;
+import io.netty.handler.codec.http.HttpObjectAggregator;
+import lombok.extern.slf4j.Slf4j;
+import org.framework.lazy.cloud.network.heartbeat.common.adapter.ChannelTypeAdapter;
+
+import java.net.URI;
+
+/**
+ * description 服务端数据处理器
+ *
+ * @author 吴佳伟
+ * @date 2023/09/13 10:27
+ */
+@Slf4j
+public class NettyTcpProxyHandler extends ChannelInboundHandlerAdapter {
+
+ private final ChannelTypeAdapter channelTypeAdapter;
+
+
+
+ public NettyTcpProxyHandler(ChannelTypeAdapter channelTypeAdapter) {
+ this.channelTypeAdapter = channelTypeAdapter;
+ }
+
+ private Channel outboundChannel;
+
+ @Override
+ public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception {
+ if (outboundChannel == null) {
+ // 假设目标服务器地址和端口
+ String targetHost = "example.com";
+ int targetPort = 80;
+
+ Bootstrap b = new Bootstrap();
+ b.group(ctx.channel().eventLoop())
+ .channel(NioSocketChannel.class)
+ .handler(new ChannelInitializer() {
+ @Override
+ protected void initChannel(SocketChannel ch) throws Exception {
+ ch.pipeline().addLast(new NettyTcpProxyBackendHandler(ctx.channel()));
+ }
+ });
+
+ ChannelFuture f = b.connect(targetHost, targetPort);
+ outboundChannel = f.channel();
+ f.addListener((ChannelFutureListener) future -> {
+ if (future.isSuccess()) {
+ outboundChannel.writeAndFlush(msg);
+ } else {
+ ctx.channel().close();
+ }
+ });
+ } else {
+ outboundChannel.writeAndFlush(msg);
+ }
+ }
+
+ @Override
+ public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+ if (outboundChannel != null) {
+ closeOnFlush(outboundChannel);
+ }
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+ cause.printStackTrace();
+ closeOnFlush(ctx.channel());
+ }
+
+ static void closeOnFlush(Channel ch) {
+ if (ch.isActive()) {
+ ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
+ }
+ }
+}
\ No newline at end of file
diff --git a/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/handler/NettyUdpProxyBackendHandler.java b/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/handler/NettyUdpProxyBackendHandler.java
new file mode 100644
index 0000000..db09297
--- /dev/null
+++ b/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/handler/NettyUdpProxyBackendHandler.java
@@ -0,0 +1,31 @@
+package org.framework.lazy.cloud.network.heartbeat.protocol.handler;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+import io.netty.channel.socket.DatagramPacket;
+
+import java.net.InetSocketAddress;
+
+
+public class NettyUdpProxyBackendHandler extends SimpleChannelInboundHandler {
+ private final Channel inboundChannel;
+ private final InetSocketAddress clientAddress;
+
+ public NettyUdpProxyBackendHandler(Channel inboundChannel, InetSocketAddress clientAddress) {
+ this.inboundChannel = inboundChannel;
+ this.clientAddress = clientAddress;
+ }
+
+ @Override
+ protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception {
+ inboundChannel.writeAndFlush(new DatagramPacket(packet.content().retain(), clientAddress));
+ ctx.channel().close();
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+ cause.printStackTrace();
+ ctx.close();
+ }
+}
\ No newline at end of file
diff --git a/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/handler/NettyUdpProxyHandler.java b/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/handler/NettyUdpProxyHandler.java
new file mode 100644
index 0000000..3cc4558
--- /dev/null
+++ b/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/handler/NettyUdpProxyHandler.java
@@ -0,0 +1,46 @@
+package org.framework.lazy.cloud.network.heartbeat.protocol.handler;
+
+import io.netty.bootstrap.Bootstrap;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+import io.netty.channel.socket.DatagramPacket;
+import io.netty.channel.socket.nio.NioDatagramChannel;
+import lombok.extern.slf4j.Slf4j;
+import org.framework.lazy.cloud.network.heartbeat.common.adapter.ChannelTypeAdapter;
+
+import java.net.InetSocketAddress;
+
+/**
+ * description 服务端数据处理器
+ *
+ * @author 吴佳伟
+ * @date 2023/09/13 10:27
+ */
+@Slf4j
+public class NettyUdpProxyHandler extends SimpleChannelInboundHandler {
+
+ private final ChannelTypeAdapter channelTypeAdapter;
+ private InetSocketAddress targetAddress = new InetSocketAddress("example.com", 80);
+
+ public NettyUdpProxyHandler(ChannelTypeAdapter channelTypeAdapter) {
+ this.channelTypeAdapter = channelTypeAdapter;
+ }
+
+ @Override
+ protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception {
+ Bootstrap b = new Bootstrap();
+ b.group(ctx.channel().eventLoop())
+ .channel(NioDatagramChannel.class)
+ .handler(new NettyUdpProxyBackendHandler(ctx.channel(), packet.sender()));
+
+ Channel channel = b.bind(0).sync().channel();
+ channel.writeAndFlush(new DatagramPacket(packet.content().retain(), targetAddress));
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+ cause.printStackTrace();
+ ctx.close();
+ }
+}
\ No newline at end of file
diff --git a/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/init/InitProtocolProxySocket.java b/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/init/InitProtocolProxySocket.java
new file mode 100644
index 0000000..40fc9e7
--- /dev/null
+++ b/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/init/InitProtocolProxySocket.java
@@ -0,0 +1,26 @@
+package org.framework.lazy.cloud.network.heartbeat.protocol.init;
+
+
+import lombok.extern.slf4j.Slf4j;
+import org.framework.lazy.cloud.network.heartbeat.protocol.context.NettyHttpProxySocketApplicationListener;
+import org.framework.lazy.cloud.network.heartbeat.protocol.context.NettyTcpProxySocketApplicationListener;
+import org.framework.lazy.cloud.network.heartbeat.protocol.context.NettyUdpProxySocketApplicationListener;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+
+
+/**
+ * description 初始化服务端
+ *
+ * @author 吴佳伟
+ * @date 2023/09/12 18:22
+ */
+@Slf4j
+@Configuration
+@Import({NettyHttpProxySocketApplicationListener.class,
+ NettyTcpProxySocketApplicationListener.class,
+ NettyUdpProxySocketApplicationListener.class})
+public class InitProtocolProxySocket {
+
+
+}
diff --git a/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/properties/ProtocolProxyProperties.java b/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/properties/ProtocolProxyProperties.java
new file mode 100644
index 0000000..4c79279
--- /dev/null
+++ b/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/java/org/framework/lazy/cloud/network/heartbeat/protocol/properties/ProtocolProxyProperties.java
@@ -0,0 +1,46 @@
+package org.framework.lazy.cloud.network.heartbeat.protocol.properties;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 服务端模式配置
+ */
+@Configuration
+@ConfigurationProperties(prefix = ProtocolProxyProperties.prefix)
+@Data
+public class ProtocolProxyProperties {
+ public static final String prefix = "spring.lazy.netty.protocol.proxy";
+
+
+ /**
+ * http协议代理
+ */
+ private HttpProtocolProxy httpProtocolProxy;
+ /**
+ * tcp协议代理
+ */
+ private TcpProtocolProxy tcpHttpProtocolProxy;
+ /**
+ * udp协议代理
+ */
+ private UdpProtocolProxy udpHttpProtocolProxy;
+
+
+
+ @Data
+ public static class HttpProtocolProxy {
+ private Integer port=8001;
+ }
+
+ @Data
+ public static class TcpProtocolProxy {
+ private Integer port=9001;
+ }
+ @Data
+ public static class UdpProtocolProxy {
+ private Integer port=10001;
+ }
+
+}
diff --git a/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/resources/META-INF/spring.factories b/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000..f240753
--- /dev/null
+++ b/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,6 @@
+# Auto Configure
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+org.framework.lazy.cloud.network.heartbeat.protocol.EnableProtocolProxyAutoConfiguration,\
+org.framework.lazy.cloud.network.heartbeat.protocol.config.ProtocolProxyAutoConfiguration,\
+org.framework.lazy.cloud.network.heartbeat.protocol.init.InitProtocolProxySocket,\
+org.framework.lazy.cloud.network.heartbeat.protocol.config.ProtocolProxyFlowConfiguration
diff --git a/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
new file mode 100644
index 0000000..dab0105
--- /dev/null
+++ b/wu-lazy-cloud-heartbeat-protocol-proxy/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -0,0 +1,4 @@
+org.framework.lazy.cloud.network.heartbeat.protocol.EnableProtocolProxyAutoConfiguration
+org.framework.lazy.cloud.network.heartbeat.protocol.config.ProtocolProxyAutoConfiguration
+org.framework.lazy.cloud.network.heartbeat.protocol.init.InitProtocolProxySocket
+org.framework.lazy.cloud.network.heartbeat.protocol.config.ProtocolProxyFlowConfiguration
diff --git a/wu-lazy-cloud-heartbeat-protocol-proxy/src/test/java/org/framework/lazy/cloud/network/heartbeat/protocol/DnsServer.java b/wu-lazy-cloud-heartbeat-protocol-proxy/src/test/java/org/framework/lazy/cloud/network/heartbeat/protocol/DnsServer.java
new file mode 100644
index 0000000..cca97f3
--- /dev/null
+++ b/wu-lazy-cloud-heartbeat-protocol-proxy/src/test/java/org/framework/lazy/cloud/network/heartbeat/protocol/DnsServer.java
@@ -0,0 +1,52 @@
+package org.framework.lazy.cloud.network.heartbeat.protocol;
+
+import io.netty.bootstrap.Bootstrap;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.DatagramChannel;
+import io.netty.channel.socket.nio.NioDatagramChannel;
+import io.netty.handler.codec.dns.DatagramDnsQueryDecoder;
+import io.netty.handler.codec.dns.DatagramDnsResponseEncoder;
+
+public class DnsServer {
+
+ private final int port;
+
+ public DnsServer(int port) {
+ this.port = port;
+ }
+
+ public void run() throws Exception {
+ EventLoopGroup group = new NioEventLoopGroup();
+ try {
+ Bootstrap b = new Bootstrap();
+ b.group(group)
+ .channel(NioDatagramChannel.class)
+ .option(ChannelOption.SO_BROADCAST, true)
+ .handler(new ChannelInitializer() {
+ @Override
+ protected void initChannel(DatagramChannel ch) throws Exception {
+ ch.pipeline().addLast(
+ new DatagramDnsQueryDecoder(),
+ new DatagramDnsResponseEncoder(),
+ new DnsServerHandler()
+ );
+ }
+ });
+
+ // 绑定端口
+ ChannelFuture f = b.bind(port).sync();
+ System.out.println("DNS server started and listening on port " + port);
+ f.channel().closeFuture().await();
+ } finally {
+ group.shutdownGracefully();
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ new DnsServer(53).run();
+ }
+}
\ No newline at end of file
diff --git a/wu-lazy-cloud-heartbeat-protocol-proxy/src/test/java/org/framework/lazy/cloud/network/heartbeat/protocol/DnsServerHandler.java b/wu-lazy-cloud-heartbeat-protocol-proxy/src/test/java/org/framework/lazy/cloud/network/heartbeat/protocol/DnsServerHandler.java
new file mode 100644
index 0000000..a8b1178
--- /dev/null
+++ b/wu-lazy-cloud-heartbeat-protocol-proxy/src/test/java/org/framework/lazy/cloud/network/heartbeat/protocol/DnsServerHandler.java
@@ -0,0 +1,84 @@
+package org.framework.lazy.cloud.network.heartbeat.protocol;
+
+import io.netty.buffer.Unpooled;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+import io.netty.handler.codec.dns.*;
+import lombok.extern.slf4j.Slf4j;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.HashMap;
+import java.util.Map;
+
+@Slf4j
+public class DnsServerHandler extends SimpleChannelInboundHandler {
+
+
+ private Map domainIpMapping = new HashMap<>();
+
+
+ {
+ domainIpMapping.put("test.wu-framework.cn.", "124.222.152.160");
+ domainIpMapping.put("www.google.com.hk.", "120.253.253.97");
+ }
+
+ @Override
+ protected void channelRead0(ChannelHandlerContext ctx, DatagramDnsQuery query) throws Exception {
+
+ // 创建DNS响应
+ DatagramDnsResponse response = new DatagramDnsResponse(query.recipient(), query.sender(), query.id());
+ DefaultDnsQuestion dnsQuestion = query.recordAt(DnsSection.QUESTION);
+ response.addRecord(DnsSection.QUESTION, dnsQuestion);
+ DnsRecordType dnsRecordType = dnsQuestion.type();
+
+ log.info("dns:{}", dnsQuestion.name());
+// log.info("query:{}", query);
+ String domain = dnsQuestion.name();
+ if (dnsRecordType == DnsRecordType.A) {
+ String ip = domainIpMapping.get(domain);
+ if (ip != null) {
+ log.info("ip:{}", ip);
+ try {
+ InetAddress address = Inet4Address.getByName(ip);
+ DefaultDnsRawRecord answer = new DefaultDnsRawRecord(
+ domain, DnsRecordType.A, 3600,
+ Unpooled.wrappedBuffer(address.getAddress())
+ );
+ response.addRecord(DnsSection.ANSWER, answer);
+ } catch (UnknownHostException e) {
+ e.printStackTrace();
+ }
+
+ // 发送响应
+ ctx.writeAndFlush(response);
+ } else {
+ // 使用系统默认 DNS 解析
+ try {
+ java.net.InetAddress[] addresses = java.net.InetAddress.getAllByName(domain);
+ for (java.net.InetAddress address : addresses) {
+ log.info("使用本地dns解析域名:{} ip:{}", domain, address.getHostAddress());
+ response.addRecord(DnsSection.ANSWER, new DefaultDnsRawRecord(
+ domain, DnsRecordType.A, 3600, Unpooled.wrappedBuffer(address.getAddress())));
+ }
+// log.info("response:{}",response.toString());
+ ctx.writeAndFlush(response);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ } else {
+ log.info("type:{}", dnsRecordType.name());
+ }
+
+
+ }
+
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+ cause.printStackTrace();
+ ctx.close();
+ }
+}
\ No newline at end of file
diff --git a/wu-lazy-cloud-heartbeat-protocol-proxy/src/test/java/org/framework/lazy/cloud/network/heartbeat/protocol/HttpProxyServer.java b/wu-lazy-cloud-heartbeat-protocol-proxy/src/test/java/org/framework/lazy/cloud/network/heartbeat/protocol/HttpProxyServer.java
new file mode 100644
index 0000000..a820795
--- /dev/null
+++ b/wu-lazy-cloud-heartbeat-protocol-proxy/src/test/java/org/framework/lazy/cloud/network/heartbeat/protocol/HttpProxyServer.java
@@ -0,0 +1,146 @@
+package org.framework.lazy.cloud.network.heartbeat.protocol;
+
+import io.netty.bootstrap.Bootstrap;
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.*;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import io.netty.channel.socket.nio.NioSocketChannel;
+import io.netty.handler.codec.http.*;
+import io.netty.handler.logging.LogLevel;
+import io.netty.handler.logging.LoggingHandler;
+
+import java.net.URI;
+
+public class HttpProxyServer {
+
+ private static final int PORT = 8080;
+
+ public static void main(String[] args) throws Exception {
+ EventLoopGroup bossGroup = new NioEventLoopGroup();
+ EventLoopGroup workerGroup = new NioEventLoopGroup();
+ try {
+ ServerBootstrap b = new ServerBootstrap();
+ b.group(bossGroup, workerGroup)
+ .channel(NioServerSocketChannel.class)
+ .handler(new LoggingHandler(LogLevel.INFO))
+ .childHandler(new ChannelInitializer() {
+ @Override
+ public void initChannel(SocketChannel ch) throws Exception {
+ ch.pipeline().addLast(
+ new HttpClientCodec(),
+ new HttpObjectAggregator(1048576),
+ new HttpProxyServerHandler()
+ );
+ }
+ });
+
+ ChannelFuture f = b.bind(PORT).sync();
+ System.out.println("Proxy server started and listening on port " + PORT);
+ f.channel().closeFuture().sync();
+ } finally {
+ bossGroup.shutdownGracefully();
+ workerGroup.shutdownGracefully();
+ }
+ }
+
+ private static class HttpProxyServerHandler extends ChannelInboundHandlerAdapter {
+
+ private Channel outboundChannel;
+
+ @Override
+ public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception {
+ if (msg instanceof FullHttpRequest) {
+ FullHttpRequest request = (FullHttpRequest) msg;
+ URI uri = new URI(request.uri());
+ String host = uri.getHost();
+ int port = uri.getPort();
+ if (port == -1) {
+ port = 80;
+ }
+
+ Bootstrap b = new Bootstrap();
+ b.group(ctx.channel().eventLoop())
+ .channel(NioSocketChannel.class)
+ .handler(new ChannelInitializer() {
+ @Override
+ public void initChannel(SocketChannel ch) throws Exception {
+ ch.pipeline().addLast(
+ new HttpClientCodec(),
+ new HttpObjectAggregator(1048576),
+ new ProxyBackendHandler(ctx.channel())
+ );
+ }
+ });
+
+ ChannelFuture f = b.connect(host, port);
+ outboundChannel = f.channel();
+ f.addListener(new ChannelFutureListener() {
+ @Override
+ public void operationComplete(ChannelFuture future) throws Exception {
+ if (future.isSuccess()) {
+ // 连接成功,将客户端请求转发到目标服务器
+ outboundChannel.writeAndFlush(request.retain());
+ } else {
+ // 连接失败,关闭客户端通道
+ ctx.channel().close();
+ }
+ }
+ });
+ }
+ }
+
+ @Override
+ public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+ if (outboundChannel != null) {
+ closeOnFlush(outboundChannel);
+ }
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+ cause.printStackTrace();
+ closeOnFlush(ctx.channel());
+ }
+
+ static void closeOnFlush(Channel ch) {
+ if (ch.isActive()) {
+ ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
+ }
+ }
+ }
+
+ private static class ProxyBackendHandler extends ChannelInboundHandlerAdapter {
+
+ private final Channel inboundChannel;
+
+ ProxyBackendHandler(Channel inboundChannel) {
+ this.inboundChannel = inboundChannel;
+ }
+
+ @Override
+ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+ // 将目标服务器的响应转发给客户端
+ inboundChannel.writeAndFlush(msg);
+ }
+
+ @Override
+ public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+ closeOnFlush(inboundChannel);
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+ cause.printStackTrace();
+ closeOnFlush(ctx.channel());
+ }
+
+ static void closeOnFlush(Channel ch) {
+ if (ch.isActive()) {
+ ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/wu-lazy-cloud-heartbeat-protocol-proxy/src/test/java/org/framework/lazy/cloud/network/heartbeat/protocol/MultiProtocolProxyServer.java b/wu-lazy-cloud-heartbeat-protocol-proxy/src/test/java/org/framework/lazy/cloud/network/heartbeat/protocol/MultiProtocolProxyServer.java
new file mode 100644
index 0000000..4a156e5
--- /dev/null
+++ b/wu-lazy-cloud-heartbeat-protocol-proxy/src/test/java/org/framework/lazy/cloud/network/heartbeat/protocol/MultiProtocolProxyServer.java
@@ -0,0 +1,312 @@
+package org.framework.lazy.cloud.network.heartbeat.protocol;
+
+import io.netty.bootstrap.Bootstrap;
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.*;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.DatagramPacket;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioDatagramChannel;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import io.netty.channel.socket.nio.NioSocketChannel;
+import io.netty.handler.codec.http.*;
+import io.netty.handler.logging.LogLevel;
+import io.netty.handler.logging.LoggingHandler;
+
+import java.net.InetSocketAddress;
+import java.net.URI;
+
+public class MultiProtocolProxyServer {
+ private static final int HTTP_PORT = 8080;
+ private static final int TCP_PORT = 9090;
+ private static final int UDP_PORT = 10000;
+
+ public static void main(String[] args) throws Exception {
+ EventLoopGroup bossGroup = new NioEventLoopGroup();
+ EventLoopGroup workerGroup = new NioEventLoopGroup();
+
+ try {
+ // 启动 HTTP 代理服务器
+ startHttpProxy(bossGroup, workerGroup);
+ // 启动 TCP 代理服务器
+ startTcpProxy(bossGroup, workerGroup);
+ // 启动 UDP 代理服务器
+ startUdpProxy(workerGroup);
+
+ System.out.println("Proxy servers started.");
+ } finally {
+ bossGroup.shutdownGracefully();
+ workerGroup.shutdownGracefully();
+ }
+ }
+
+ private static void startHttpProxy(EventLoopGroup bossGroup, EventLoopGroup workerGroup) throws InterruptedException {
+ ServerBootstrap b = new ServerBootstrap();
+ b.group(bossGroup, workerGroup)
+ .channel(NioServerSocketChannel.class)
+ .handler(new LoggingHandler(LogLevel.INFO))
+ .childHandler(new ChannelInitializer() {
+ @Override
+ protected void initChannel(SocketChannel ch) throws Exception {
+ ch.pipeline().addLast(
+ new HttpClientCodec(),
+ new HttpObjectAggregator(1048576),
+ new HttpProxyServerHandler()
+ );
+ }
+ });
+
+ b.bind(HTTP_PORT).sync();
+ System.out.println("HTTP proxy server started on port " + HTTP_PORT);
+ }
+
+ private static void startTcpProxy(EventLoopGroup bossGroup, EventLoopGroup workerGroup) throws InterruptedException {
+ ServerBootstrap b = new ServerBootstrap();
+ b.group(bossGroup, workerGroup)
+ .channel(NioServerSocketChannel.class)
+ .handler(new LoggingHandler(LogLevel.INFO))
+ .childHandler(new ChannelInitializer() {
+ @Override
+ protected void initChannel(SocketChannel ch) throws Exception {
+ ch.pipeline().addLast(new TcpProxyServerHandler());
+ }
+ });
+
+ b.bind(TCP_PORT).sync();
+ System.out.println("TCP proxy server started on port " + TCP_PORT);
+ }
+
+ private static void startUdpProxy(EventLoopGroup workerGroup) throws InterruptedException {
+ Bootstrap b = new Bootstrap();
+ b.group(workerGroup)
+ .channel(NioDatagramChannel.class)
+ .handler(new UdpProxyServerHandler());
+
+ b.bind(UDP_PORT).sync();
+ System.out.println("UDP proxy server started on port " + UDP_PORT);
+ }
+
+ private static class HttpProxyServerHandler extends ChannelInboundHandlerAdapter {
+ private Channel outboundChannel;
+
+ @Override
+ public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception {
+ if (msg instanceof FullHttpRequest) {
+ FullHttpRequest request = (FullHttpRequest) msg;
+ URI uri = new URI(request.uri());
+ String host = uri.getHost();
+ int port = uri.getPort();
+ if (port == -1) {
+ port = 80;
+ }
+
+ Bootstrap b = new Bootstrap();
+ b.group(ctx.channel().eventLoop())
+ .channel(NioSocketChannel.class)
+ .handler(new ChannelInitializer() {
+ @Override
+ protected void initChannel(SocketChannel ch) throws Exception {
+ ch.pipeline().addLast(
+ new HttpClientCodec(),
+ new HttpObjectAggregator(1048576),
+ new HttpProxyBackendHandler(ctx.channel())
+ );
+ }
+ });
+
+ ChannelFuture f = b.connect(host, port);
+ outboundChannel = f.channel();
+ f.addListener(new ChannelFutureListener() {
+ @Override
+ public void operationComplete(ChannelFuture future) throws Exception {
+ if (future.isSuccess()) {
+ outboundChannel.writeAndFlush(request.retain());
+ } else {
+ ctx.channel().close();
+ }
+ }
+ });
+ }
+ }
+
+ @Override
+ public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+ if (outboundChannel != null) {
+ closeOnFlush(outboundChannel);
+ }
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+ cause.printStackTrace();
+ closeOnFlush(ctx.channel());
+ }
+
+ static void closeOnFlush(Channel ch) {
+ if (ch.isActive()) {
+ ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
+ }
+ }
+ }
+
+ private static class HttpProxyBackendHandler extends ChannelInboundHandlerAdapter {
+ private final Channel inboundChannel;
+
+ HttpProxyBackendHandler(Channel inboundChannel) {
+ this.inboundChannel = inboundChannel;
+ }
+
+ @Override
+ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+ inboundChannel.writeAndFlush(msg);
+ }
+
+ @Override
+ public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+ closeOnFlush(inboundChannel);
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+ cause.printStackTrace();
+ closeOnFlush(ctx.channel());
+ }
+
+ static void closeOnFlush(Channel ch) {
+ if (ch.isActive()) {
+ ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
+ }
+ }
+ }
+
+ private static class TcpProxyServerHandler extends ChannelInboundHandlerAdapter {
+ private Channel outboundChannel;
+
+ @Override
+ public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception {
+ if (outboundChannel == null) {
+ // 假设目标服务器地址和端口
+ String targetHost = "example.com";
+ int targetPort = 80;
+
+ Bootstrap b = new Bootstrap();
+ b.group(ctx.channel().eventLoop())
+ .channel(NioSocketChannel.class)
+ .handler(new ChannelInitializer() {
+ @Override
+ protected void initChannel(SocketChannel ch) throws Exception {
+ ch.pipeline().addLast(new TcpProxyBackendHandler(ctx.channel()));
+ }
+ });
+
+ ChannelFuture f = b.connect(targetHost, targetPort);
+ outboundChannel = f.channel();
+ f.addListener(new ChannelFutureListener() {
+ @Override
+ public void operationComplete(ChannelFuture future) throws Exception {
+ if (future.isSuccess()) {
+ outboundChannel.writeAndFlush(msg);
+ } else {
+ ctx.channel().close();
+ }
+ }
+ });
+ } else {
+ outboundChannel.writeAndFlush(msg);
+ }
+ }
+
+ @Override
+ public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+ if (outboundChannel != null) {
+ closeOnFlush(outboundChannel);
+ }
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+ cause.printStackTrace();
+ closeOnFlush(ctx.channel());
+ }
+
+ static void closeOnFlush(Channel ch) {
+ if (ch.isActive()) {
+ ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
+ }
+ }
+ }
+
+ private static class TcpProxyBackendHandler extends ChannelInboundHandlerAdapter {
+ private final Channel inboundChannel;
+
+ TcpProxyBackendHandler(Channel inboundChannel) {
+ this.inboundChannel = inboundChannel;
+ }
+
+ @Override
+ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+ inboundChannel.writeAndFlush(msg);
+ }
+
+ @Override
+ public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+ closeOnFlush(inboundChannel);
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+ cause.printStackTrace();
+ closeOnFlush(ctx.channel());
+ }
+
+ static void closeOnFlush(Channel ch) {
+ if (ch.isActive()) {
+ ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
+ }
+ }
+ }
+
+ private static class UdpProxyServerHandler extends SimpleChannelInboundHandler {
+ private InetSocketAddress targetAddress = new InetSocketAddress("example.com", 80);
+
+ @Override
+ protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception {
+ Bootstrap b = new Bootstrap();
+ b.group(ctx.channel().eventLoop())
+ .channel(NioDatagramChannel.class)
+ .handler(new UdpProxyBackendHandler(ctx.channel(), packet.sender()));
+
+ Channel channel = b.bind(0).sync().channel();
+ channel.writeAndFlush(new DatagramPacket(packet.content().retain(), targetAddress));
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+ cause.printStackTrace();
+ ctx.close();
+ }
+ }
+
+ private static class UdpProxyBackendHandler extends SimpleChannelInboundHandler {
+ private final Channel inboundChannel;
+ private final InetSocketAddress clientAddress;
+
+ UdpProxyBackendHandler(Channel inboundChannel, InetSocketAddress clientAddress) {
+ this.inboundChannel = inboundChannel;
+ this.clientAddress = clientAddress;
+ }
+
+ @Override
+ protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception {
+ inboundChannel.writeAndFlush(new DatagramPacket(packet.content().retain(), clientAddress));
+ ctx.channel().close();
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+ cause.printStackTrace();
+ ctx.close();
+ }
+ }
+}
\ No newline at end of file
diff --git a/wu-lazy-cloud-heartbeat-protocol-proxy/src/test/java/org/framework/lazy/cloud/network/heartbeat/protocol/demo/DnsServer.java b/wu-lazy-cloud-heartbeat-protocol-proxy/src/test/java/org/framework/lazy/cloud/network/heartbeat/protocol/demo/DnsServer.java
new file mode 100644
index 0000000..292cef7
--- /dev/null
+++ b/wu-lazy-cloud-heartbeat-protocol-proxy/src/test/java/org/framework/lazy/cloud/network/heartbeat/protocol/demo/DnsServer.java
@@ -0,0 +1,124 @@
+package org.framework.lazy.cloud.network.heartbeat.protocol.demo;
+
+
+import com.github.xiaoymin.knife4j.core.util.StrUtil;
+import io.netty.bootstrap.Bootstrap;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.*;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.nio.NioDatagramChannel;
+import io.netty.handler.codec.dns.*;
+import io.netty.util.AttributeKey;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
+
+@Slf4j
+public final class DnsServer {
+
+ private static final List BLACK_LIST_DOMAIN = new ArrayList<>();
+
+ static {
+
+ String s;
+ try (InputStream is = DnsServer.class.getClassLoader().getResourceAsStream("black_list.txt");
+ BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
+ while (StrUtil.isNotBlank(s = br.readLine())) {
+ BLACK_LIST_DOMAIN.add(s);
+ }
+ } catch (Exception e) {
+ log.error(e.getMessage(), e);
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ ProxyUdp proxyUdp = new ProxyUdp();
+ proxyUdp.init();
+ final int[] num = {0};
+ final NioEventLoopGroup group = new NioEventLoopGroup();
+ Bootstrap bootstrap = new Bootstrap();
+ bootstrap.group(group).channel(NioDatagramChannel.class)
+ .handler(new ChannelInitializer() {
+ @Override
+ protected void initChannel(NioDatagramChannel nioDatagramChannel) {
+ nioDatagramChannel.pipeline().addLast(new DatagramDnsQueryDecoder());
+ nioDatagramChannel.pipeline().addLast(new SimpleChannelInboundHandler() {
+
+ @Override
+ protected void channelRead0(ChannelHandlerContext ctx, DatagramDnsQuery msg) {
+ try {
+ DefaultDnsQuestion dnsQuestion = msg.recordAt(DnsSection.QUESTION);
+ String name = dnsQuestion.name();
+ log.info(name + ++num[0]);
+ Channel channel = ctx.channel();
+ int id = msg.id();
+ channel.attr(AttributeKey.valueOf(String.valueOf(id))).set(msg);
+ if (BLACK_LIST_DOMAIN.contains(name)) {
+ DnsQuestion question = msg.recordAt(DnsSection.QUESTION);
+ DatagramDnsResponse dnsResponse = getDatagramDnsResponse(msg, id, question);
+ channel.writeAndFlush(dnsResponse);
+ return;
+ }
+ proxyUdp.send(name, msg.id(), channel);
+ } catch (Exception e) {
+ log.error(e.getMessage(), e);
+ }
+ }
+
+ private DatagramDnsResponse getDatagramDnsResponse(DatagramDnsQuery msg, int id, DnsQuestion question) {
+ DatagramDnsResponse dnsResponse = new DatagramDnsResponse(msg.recipient(), msg.sender(), id);
+ dnsResponse.addRecord(DnsSection.QUESTION, question);
+
+ InetAddress inetAddress = null;
+ try {
+ inetAddress = Inet4Address.getByName("www.baidu.com");
+ } catch (UnknownHostException e) {
+ throw new RuntimeException(e);
+ }
+ byte[] ipBytes = inetAddress.getAddress();
+ // just print the IP after query
+// DefaultDnsRawRecord queryAnswer = new DefaultDnsRawRecord(
+// question.name(),
+// DnsRecordType.A, 600, Unpooled.wrappedBuffer(new byte[]{(byte) 192, (byte) 168, 1, 1}));
+
+ DefaultDnsRawRecord queryAnswer = new DefaultDnsRawRecord(
+ question.name(),
+ DnsRecordType.A,
+ 600,
+ Unpooled.wrappedBuffer(ipBytes)
+ );
+
+ dnsResponse.addRecord(DnsSection.ANSWER, queryAnswer);
+ return dnsResponse;
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable e) {
+ log.error(e.getMessage(), e);
+ }
+ });
+ nioDatagramChannel.pipeline().addLast(new DatagramDnsResponseEncoder());
+
+ }
+ }).option(ChannelOption.SO_BROADCAST, true);
+
+ int port = 53;
+ ChannelFuture future = bootstrap.bind(port).addListener(future1 -> {
+ log.info("server listening port:{}", port);
+ });
+
+ future.channel().closeFuture().addListener(future1 -> {
+ if (future.isSuccess()) {
+ log.info(future.channel().toString());
+ }
+ });
+ }
+}
+
diff --git a/wu-lazy-cloud-heartbeat-protocol-proxy/src/test/java/org/framework/lazy/cloud/network/heartbeat/protocol/demo/ProxyUdp.java b/wu-lazy-cloud-heartbeat-protocol-proxy/src/test/java/org/framework/lazy/cloud/network/heartbeat/protocol/demo/ProxyUdp.java
new file mode 100644
index 0000000..5850dc5
--- /dev/null
+++ b/wu-lazy-cloud-heartbeat-protocol-proxy/src/test/java/org/framework/lazy/cloud/network/heartbeat/protocol/demo/ProxyUdp.java
@@ -0,0 +1,80 @@
+package org.framework.lazy.cloud.network.heartbeat.protocol.demo;
+
+import io.netty.bootstrap.Bootstrap;
+import io.netty.buffer.ByteBufUtil;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.*;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.DatagramChannel;
+import io.netty.channel.socket.nio.NioDatagramChannel;
+import io.netty.handler.codec.dns.*;
+import io.netty.util.AttributeKey;
+import lombok.extern.slf4j.Slf4j;
+
+import java.net.InetSocketAddress;
+
+@Slf4j
+class ProxyUdp {
+ private Channel localChannel;
+ private Channel proxyChannel;
+
+ public void init() throws InterruptedException {
+ EventLoopGroup proxyGroup = new NioEventLoopGroup();
+ Bootstrap b = new Bootstrap();
+ b.group(proxyGroup)
+ .channel(NioDatagramChannel.class)
+ .handler(new ChannelInitializer() {
+ @Override
+ protected void initChannel(DatagramChannel ch) {
+ ChannelPipeline p = ch.pipeline();
+ p.addLast(new DatagramDnsQueryEncoder())
+ .addLast(new DatagramDnsResponseDecoder())
+ .addLast(new SimpleChannelInboundHandler() {
+ @Override
+ public void channelActive(ChannelHandlerContext ctx) {
+ log.info(ctx.channel().toString());
+ }
+
+ @Override
+ protected void channelRead0(ChannelHandlerContext ctx, DatagramDnsResponse msg) {
+ DatagramDnsQuery dnsQuery = localChannel.attr(AttributeKey.valueOf(String.valueOf(msg.id()))).get();
+ DnsQuestion question = msg.recordAt(DnsSection.QUESTION);
+ DatagramDnsResponse dnsResponse = new DatagramDnsResponse(dnsQuery.recipient(), dnsQuery.sender(), msg.id());
+ dnsResponse.addRecord(DnsSection.QUESTION, question);
+
+ for (int i = 0, count = msg.count(DnsSection.ANSWER); i < count; i++) {
+ DnsRecord record = msg.recordAt(DnsSection.ANSWER, i);
+ if (record.type() == DnsRecordType.A) {
+ // just print the IP after query
+ DnsRawRecord raw = (DnsRawRecord) record;
+ DefaultDnsRawRecord queryAnswer = new DefaultDnsRawRecord(
+ question.name(),
+ DnsRecordType.A, 600, Unpooled.wrappedBuffer(ByteBufUtil.getBytes(raw.content())));
+ dnsResponse.addRecord(DnsSection.ANSWER, queryAnswer);
+ }
+ }
+
+ localChannel.writeAndFlush(dnsResponse);
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable e) {
+ log.error(e.getMessage(), e);
+ }
+ });
+
+ }
+ });
+ proxyChannel = b.bind(0).sync().addListener(future1 -> {
+ log.info("绑定成功");
+ }).channel();
+ }
+
+ public void send(String domain, int id, Channel localChannel) {
+ this.localChannel = localChannel;
+ DnsQuery query = new DatagramDnsQuery(null, new InetSocketAddress("114.114.114.114", 53), id).setRecord(
+ DnsSection.QUESTION,
+ new DefaultDnsQuestion(domain, DnsRecordType.A));
+ this.proxyChannel.writeAndFlush(query);
+ }
+}
\ No newline at end of file