From f5d027cc06edcb714251a4bc8dbc2405cbeb45ee Mon Sep 17 00:00:00 2001
From: wujiawei <1207537021@qq.com>
Date: Sun, 12 Feb 2023 09:10:47 +0800
Subject: [PATCH] init
---
README.md | 12 +-
netty-proxy-client/pom.xml | 40 +++++++
.../java/com/luck/client/ClientStart.java | 17 +++
.../main/java/com/luck/client/Constant.java | 87 ++++++++++++++
.../java/com/luck/client/ProxyHandler.java | 98 +++++++++++++++
.../java/com/luck/client/ProxySocket.java | 101 ++++++++++++++++
.../java/com/luck/client/RealHandler.java | 74 ++++++++++++
.../main/java/com/luck/client/RealSocket.java | 59 +++++++++
.../client/controller/ClientController.java | 17 +++
.../src/main/resources/application.yml | 2 +
netty-proxy-common/pom.xml | 26 ++++
.../src/main/java/com/luck/msg/MyMsg.java | 58 +++++++++
.../main/java/com/luck/msg/MyMsgDecoder.java | 41 +++++++
.../main/java/com/luck/msg/MyMsgEncoder.java | 28 +++++
netty-proxy-server/pom.xml | 40 +++++++
.../java/com/luck/server/ClientHandler.java | 113 ++++++++++++++++++
.../main/java/com/luck/server/Constant.java | 82 +++++++++++++
.../java/com/luck/server/ServerSocket.java | 61 ++++++++++
.../java/com/luck/server/ServerStart.java | 17 +++
.../java/com/luck/server/VisitorHandler.java | 103 ++++++++++++++++
.../java/com/luck/server/VisitorSocket.java | 37 ++++++
.../server/controller/ServerController.java | 17 +++
.../src/main/resources/application.yml | 2 +
pom.xml | 68 +++++++++++
24 files changed, 1191 insertions(+), 9 deletions(-)
create mode 100644 netty-proxy-client/pom.xml
create mode 100644 netty-proxy-client/src/main/java/com/luck/client/ClientStart.java
create mode 100644 netty-proxy-client/src/main/java/com/luck/client/Constant.java
create mode 100644 netty-proxy-client/src/main/java/com/luck/client/ProxyHandler.java
create mode 100644 netty-proxy-client/src/main/java/com/luck/client/ProxySocket.java
create mode 100644 netty-proxy-client/src/main/java/com/luck/client/RealHandler.java
create mode 100644 netty-proxy-client/src/main/java/com/luck/client/RealSocket.java
create mode 100644 netty-proxy-client/src/main/java/com/luck/client/controller/ClientController.java
create mode 100644 netty-proxy-client/src/main/resources/application.yml
create mode 100644 netty-proxy-common/pom.xml
create mode 100644 netty-proxy-common/src/main/java/com/luck/msg/MyMsg.java
create mode 100644 netty-proxy-common/src/main/java/com/luck/msg/MyMsgDecoder.java
create mode 100644 netty-proxy-common/src/main/java/com/luck/msg/MyMsgEncoder.java
create mode 100644 netty-proxy-server/pom.xml
create mode 100644 netty-proxy-server/src/main/java/com/luck/server/ClientHandler.java
create mode 100644 netty-proxy-server/src/main/java/com/luck/server/Constant.java
create mode 100644 netty-proxy-server/src/main/java/com/luck/server/ServerSocket.java
create mode 100644 netty-proxy-server/src/main/java/com/luck/server/ServerStart.java
create mode 100644 netty-proxy-server/src/main/java/com/luck/server/VisitorHandler.java
create mode 100644 netty-proxy-server/src/main/java/com/luck/server/VisitorSocket.java
create mode 100644 netty-proxy-server/src/main/java/com/luck/server/controller/ServerController.java
create mode 100644 netty-proxy-server/src/main/resources/application.yml
create mode 100644 pom.xml
diff --git a/README.md b/README.md
index 4fa9a7d..f7def0a 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,4 @@
-
-原理分析
-内网穿透的实现过程主要分三步
+原理分析 内网穿透的实现过程主要分三步
1、启动服务端,这时服务端监听了两个端口(16001,16002,可根据启动参数修改),
@@ -14,10 +12,6 @@
这三步最终形成了(访客-代理-客户端-真实服务)完整的通道。
+启动服务端 java -jar cc-server.jar 16001 16002
-启动服务端
-java -jar cc-server.jar 16001 16002
-
-
-启动客户端
-java -jar cc-client.jar 127.0.0.1 16001 8080
+启动客户端 java -jar cc-client.jar 127.0.0.1 16001 8080
diff --git a/netty-proxy-client/pom.xml b/netty-proxy-client/pom.xml
new file mode 100644
index 0000000..db5ff27
--- /dev/null
+++ b/netty-proxy-client/pom.xml
@@ -0,0 +1,40 @@
+
+
+
+ netty-proxy
+ com.wu
+ 0.0.1-SNAPSHOT
+
+ 4.0.0
+
+ netty-proxy-client
+
+
+ 8
+ 8
+
+
+
+ com.wu
+ netty-proxy-common
+ 0.0.1-SNAPSHOT
+
+
+ top.wu2020
+ wu-framework-web
+ 1.1.9-JDK1.8-SNAPSHOT
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ io.springfox
+ springfox-boot-starter
+ 3.0.0
+
+
+
+
\ No newline at end of file
diff --git a/netty-proxy-client/src/main/java/com/luck/client/ClientStart.java b/netty-proxy-client/src/main/java/com/luck/client/ClientStart.java
new file mode 100644
index 0000000..3117db7
--- /dev/null
+++ b/netty-proxy-client/src/main/java/com/luck/client/ClientStart.java
@@ -0,0 +1,17 @@
+package com.luck.client;
+
+public class ClientStart {
+
+ public static void main(String[] args) throws Exception {
+ if (null != args && args.length == 3) {
+ int realPort = Integer.parseInt(args[2]);
+ int serverPort = Integer.parseInt(args[1]);
+ String serverIp = args[0];
+ Constant.serverIp = serverIp;
+ Constant.serverPort = serverPort;
+ Constant.realPort = realPort;
+ }
+ // 连接代理服务
+ ProxySocket.connectProxyServer();
+ }
+}
\ No newline at end of file
diff --git a/netty-proxy-client/src/main/java/com/luck/client/Constant.java b/netty-proxy-client/src/main/java/com/luck/client/Constant.java
new file mode 100644
index 0000000..18e09a7
--- /dev/null
+++ b/netty-proxy-client/src/main/java/com/luck/client/Constant.java
@@ -0,0 +1,87 @@
+package com.luck.client;
+
+import io.netty.channel.Channel;
+import io.netty.util.AttributeKey;
+import io.netty.util.internal.StringUtil;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class Constant {
+ /**
+ * 绑定访客id
+ */
+ public static final AttributeKey VID = AttributeKey.newInstance("vid");
+ /**
+ * 代理服务channel
+ */
+ public static Channel proxyChannel = null;
+ /**
+ * 访客,代理服务channel
+ */
+ public static Map vpc = new ConcurrentHashMap<>();
+
+ /**
+ * 访客,真实服务channel
+ */
+ public static Map vrc = new ConcurrentHashMap<>();
+
+ /**
+ * 真实服务端口
+ */
+ public static int realPort = 8080;
+
+ /**
+ * 服务端口
+ */
+ public static int serverPort = 16001;
+
+ /**
+ * 服务IP
+ */
+ public static String serverIp = "127.0.0.1";
+
+ /**
+ * 清除连接
+ *
+ * @param vid 访客ID
+ */
+ public static void clearvpcvrc(String vid) {
+ if (StringUtil.isNullOrEmpty(vid)) {
+ return;
+ }
+ Channel clientChannel = vpc.get(vid);
+ if (null != clientChannel) {
+ clientChannel.attr(VID).set(null);
+ vpc.remove(vid);
+ }
+ Channel visitorChannel = vrc.get(vid);
+ if (null != visitorChannel) {
+ visitorChannel.attr(VID).set(null);
+ vrc.remove(vid);
+ }
+ }
+
+ /**
+ * 清除关闭连接
+ *
+ * @param vid 访客ID
+ */
+ public static void clearvpcvrcAndClose(String vid) {
+ if (StringUtil.isNullOrEmpty(vid)) {
+ return;
+ }
+ Channel clientChannel = vpc.get(vid);
+ if (null != clientChannel) {
+ clientChannel.attr(VID).set(null);
+ vpc.remove(vid);
+ clientChannel.close();
+ }
+ Channel visitorChannel = vrc.get(vid);
+ if (null != visitorChannel) {
+ visitorChannel.attr(VID).set(null);
+ vrc.remove(vid);
+ visitorChannel.close();
+ }
+ }
+}
\ No newline at end of file
diff --git a/netty-proxy-client/src/main/java/com/luck/client/ProxyHandler.java b/netty-proxy-client/src/main/java/com/luck/client/ProxyHandler.java
new file mode 100644
index 0000000..35932bc
--- /dev/null
+++ b/netty-proxy-client/src/main/java/com/luck/client/ProxyHandler.java
@@ -0,0 +1,98 @@
+package com.luck.client;
+
+import com.luck.msg.MyMsg;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.SimpleChannelInboundHandler;
+import io.netty.handler.timeout.IdleStateEvent;
+import io.netty.util.internal.StringUtil;
+
+public class ProxyHandler extends SimpleChannelInboundHandler {
+
+ @Override
+ public void channelRead0(ChannelHandlerContext ctx, MyMsg myMsg) {
+ // 客户端读取到代理过来的数据了
+ byte type = myMsg.getType();
+ String vid = new String(myMsg.getData());
+ switch (type) {
+ case MyMsg.TYPE_HEARTBEAT:
+ break;
+ case MyMsg.TYPE_CONNECT:
+ RealSocket.connectRealServer(vid);
+ break;
+ case MyMsg.TYPE_DISCONNECT:
+ Constant.clearvpcvrcAndClose(vid);
+ break;
+ case MyMsg.TYPE_TRANSFER:
+ // 把数据转到真实服务
+ ByteBuf buf = ctx.alloc().buffer(myMsg.getData().length);
+ buf.writeBytes(myMsg.getData());
+
+ String visitorId = ctx.channel().attr(Constant.VID).get();
+ Channel rchannel = Constant.vrc.get(visitorId);
+ if (null != rchannel) {
+ rchannel.writeAndFlush(buf);
+ }
+ break;
+ default:
+ // 操作有误
+ }
+ // 客户端发数据到真实服务了
+ }
+
+ @Override
+ public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
+ String vid = ctx.channel().attr(Constant.VID).get();
+ if (StringUtil.isNullOrEmpty(vid)) {
+ super.channelWritabilityChanged(ctx);
+ return;
+ }
+ Channel realChannel = Constant.vrc.get(vid);
+ if (realChannel != null) {
+ realChannel.config().setOption(ChannelOption.AUTO_READ, ctx.channel().isWritable());
+ }
+
+ super.channelWritabilityChanged(ctx);
+ }
+
+ @Override
+ public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+ String vid = ctx.channel().attr(Constant.VID).get();
+ if (StringUtil.isNullOrEmpty(vid)) {
+ super.channelInactive(ctx);
+ return;
+ }
+ Channel realChannel = Constant.vrc.get(vid);
+ if (realChannel != null && realChannel.isActive()) {
+ realChannel.close();
+ }
+ super.channelInactive(ctx);
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+ super.exceptionCaught(ctx, cause);
+ cause.printStackTrace();
+ }
+
+ @Override
+ public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
+ if (evt instanceof IdleStateEvent) {
+ IdleStateEvent event = (IdleStateEvent) evt;
+ switch (event.state()) {
+ case READER_IDLE:
+ ctx.channel().close();
+ break;
+ case WRITER_IDLE:
+ MyMsg myMsg = new MyMsg();
+ myMsg.setType(MyMsg.TYPE_HEARTBEAT);
+ ctx.channel().writeAndFlush(myMsg);
+ break;
+ case ALL_IDLE:
+ break;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/netty-proxy-client/src/main/java/com/luck/client/ProxySocket.java b/netty-proxy-client/src/main/java/com/luck/client/ProxySocket.java
new file mode 100644
index 0000000..236a2a7
--- /dev/null
+++ b/netty-proxy-client/src/main/java/com/luck/client/ProxySocket.java
@@ -0,0 +1,101 @@
+package com.luck.client;
+
+import com.luck.msg.MyMsg;
+import com.luck.msg.MyMsgDecoder;
+import com.luck.msg.MyMsgEncoder;
+import io.netty.bootstrap.Bootstrap;
+import io.netty.channel.*;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioSocketChannel;
+import io.netty.handler.timeout.IdleStateHandler;
+import io.netty.util.internal.StringUtil;
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+public class ProxySocket {
+ /**
+ * 重连代理服务
+ */
+ private static final ScheduledExecutorService reconnectExecutor = Executors.newSingleThreadScheduledExecutor();
+ private static EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
+
+ public static Channel connectProxyServer() throws Exception {
+ reconnectExecutor.scheduleAtFixedRate(() -> {
+ try {
+ connectProxyServer(null);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }, 3, 3, TimeUnit.SECONDS);
+ return connectProxyServer(null);
+ }
+
+ public static Channel connectProxyServer(String vid) throws Exception {
+ if (StringUtil.isNullOrEmpty(vid)) {
+ if (Constant.proxyChannel == null || !Constant.proxyChannel.isActive()) {
+ newConnect(null);
+ }
+ return null;
+ } else {
+ Channel channel = Constant.vpc.get(vid);
+ if (null == channel) {
+ newConnect(vid);
+ channel = Constant.vpc.get(vid);
+ }
+ return channel;
+ }
+ }
+
+ private static void newConnect(String vid) throws InterruptedException {
+ Bootstrap bootstrap = new Bootstrap();
+ bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class)
+ .handler(new ChannelInitializer() {
+ @Override
+ public void initChannel(SocketChannel ch) throws Exception {
+ ChannelPipeline pipeline = ch.pipeline();
+ pipeline.addLast(new MyMsgDecoder(Integer.MAX_VALUE, 0, 4, -4, 0));
+ pipeline.addLast(new MyMsgEncoder());
+ pipeline.addLast(new IdleStateHandler(40, 8, 0));
+ pipeline.addLast(new ProxyHandler());
+ }
+ });
+
+ bootstrap.connect(Constant.serverIp, Constant.serverPort).addListener(new ChannelFutureListener() {
+ @Override
+ public void operationComplete(ChannelFuture future) throws Exception {
+ if (future.isSuccess()) {
+ // 客户端链接代理服务器成功
+ Channel channel = future.channel();
+ if (StringUtil.isNullOrEmpty(vid)) {
+ // 告诉服务端这条连接是client的连接
+ MyMsg myMsg = new MyMsg();
+ myMsg.setType(MyMsg.TYPE_CONNECT);
+ myMsg.setData("client".getBytes());
+ channel.writeAndFlush(myMsg);
+
+ Constant.proxyChannel = channel;
+ } else {
+
+ // 告诉服务端这条连接是vid的连接
+ MyMsg myMsg = new MyMsg();
+ myMsg.setType(MyMsg.TYPE_CONNECT);
+ myMsg.setData(vid.getBytes());
+ channel.writeAndFlush(myMsg);
+
+ // 客户端绑定通道关系
+ Constant.vpc.put(vid, channel);
+ channel.attr(Constant.VID).set(vid);
+
+ Channel realChannel = Constant.vrc.get(vid);
+ if (null != realChannel) {
+ realChannel.config().setOption(ChannelOption.AUTO_READ, true);
+ }
+ }
+ }
+ }
+ });
+ }
+}
\ No newline at end of file
diff --git a/netty-proxy-client/src/main/java/com/luck/client/RealHandler.java b/netty-proxy-client/src/main/java/com/luck/client/RealHandler.java
new file mode 100644
index 0000000..5f1d679
--- /dev/null
+++ b/netty-proxy-client/src/main/java/com/luck/client/RealHandler.java
@@ -0,0 +1,74 @@
+package com.luck.client;
+
+import com.luck.msg.MyMsg;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.SimpleChannelInboundHandler;
+import io.netty.util.internal.StringUtil;
+
+public class RealHandler extends SimpleChannelInboundHandler {
+
+ @Override
+ public void channelRead0(ChannelHandlerContext ctx, ByteBuf buf) {
+ // 客户读取到真实服务数据了
+ byte[] bytes = new byte[buf.readableBytes()];
+ buf.readBytes(bytes);
+ MyMsg myMsg = new MyMsg();
+ myMsg.setType(MyMsg.TYPE_TRANSFER);
+ myMsg.setData(bytes);
+ String vid = ctx.channel().attr(Constant.VID).get();
+ if (StringUtil.isNullOrEmpty(vid)) {
+ return;
+ }
+ Channel proxyChannel = Constant.vpc.get(vid);
+ if (null != proxyChannel) {
+ proxyChannel.writeAndFlush(myMsg);
+ }
+ // 客户端发送真实数据到代理了
+ }
+
+ @Override
+ public void channelActive(ChannelHandlerContext ctx) throws Exception {
+ super.channelActive(ctx);
+ }
+
+ @Override
+ public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+ String vid = ctx.channel().attr(Constant.VID).get();
+ if (StringUtil.isNullOrEmpty(vid)) {
+ super.channelInactive(ctx);
+ return;
+ }
+ Channel proxyChannel = Constant.vpc.get(vid);
+ if (proxyChannel != null) {
+ MyMsg myMsg = new MyMsg();
+ myMsg.setType(MyMsg.TYPE_DISCONNECT);
+ myMsg.setData(vid.getBytes());
+ proxyChannel.writeAndFlush(myMsg);
+ }
+
+ super.channelInactive(ctx);
+ }
+
+ @Override
+ public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
+ String vid = ctx.channel().attr(Constant.VID).get();
+ if (StringUtil.isNullOrEmpty(vid)) {
+ super.channelWritabilityChanged(ctx);
+ return;
+ }
+ Channel proxyChannel = Constant.vpc.get(vid);
+ if (proxyChannel != null) {
+ proxyChannel.config().setOption(ChannelOption.AUTO_READ, ctx.channel().isWritable());
+ }
+
+ super.channelWritabilityChanged(ctx);
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+ super.exceptionCaught(ctx, cause);
+ }
+}
\ No newline at end of file
diff --git a/netty-proxy-client/src/main/java/com/luck/client/RealSocket.java b/netty-proxy-client/src/main/java/com/luck/client/RealSocket.java
new file mode 100644
index 0000000..2b5d84f
--- /dev/null
+++ b/netty-proxy-client/src/main/java/com/luck/client/RealSocket.java
@@ -0,0 +1,59 @@
+package com.luck.client;
+
+import io.netty.bootstrap.Bootstrap;
+import io.netty.channel.*;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioSocketChannel;
+import io.netty.util.internal.StringUtil;
+
+public class RealSocket {
+ static EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
+
+ /**
+ * 连接真实服务
+ *
+ * @param vid 访客ID
+ * @return
+ */
+ public static Channel connectRealServer(String vid) {
+ if (StringUtil.isNullOrEmpty(vid)) {
+ return null;
+ }
+ Channel channel = Constant.vrc.get(vid);
+ if (null == channel) {
+ newConnect(vid);
+ channel = Constant.vrc.get(vid);
+ }
+ return channel;
+ }
+
+ private static void newConnect(String vid) {
+ try {
+ Bootstrap bootstrap = new Bootstrap();
+ bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class)
+ .handler(new ChannelInitializer() {
+ @Override
+ public void initChannel(SocketChannel ch) throws Exception {
+ ChannelPipeline pipeline = ch.pipeline();
+ pipeline.addLast(new RealHandler());
+ }
+
+ });
+ bootstrap.connect("127.0.0.1", Constant.realPort).addListener(new ChannelFutureListener() {
+ @Override
+ public void operationComplete(ChannelFuture future) throws Exception {
+ if (future.isSuccess()) {
+ // 客户端链接真实服务成功
+ future.channel().config().setOption(ChannelOption.AUTO_READ, false);
+ future.channel().attr(Constant.VID).set(vid);
+ Constant.vrc.put(vid, future.channel());
+ ProxySocket.connectProxyServer(vid);
+ }
+ }
+ });
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
\ No newline at end of file
diff --git a/netty-proxy-client/src/main/java/com/luck/client/controller/ClientController.java b/netty-proxy-client/src/main/java/com/luck/client/controller/ClientController.java
new file mode 100644
index 0000000..7d33c6a
--- /dev/null
+++ b/netty-proxy-client/src/main/java/com/luck/client/controller/ClientController.java
@@ -0,0 +1,17 @@
+package com.luck.client.controller;
+
+import com.wu.framework.inner.layer.web.EasyController;
+import com.wu.framework.response.Result;
+import com.wu.framework.response.ResultFactory;
+import io.swagger.annotations.Api;
+import org.springframework.web.bind.annotation.GetMapping;
+
+@Api(tags = "客户端client")
+@EasyController("/client")
+public class ClientController {
+
+ @GetMapping("/version")
+ public Result version() {
+ return ResultFactory.successOf("客户端版本");
+ }
+}
diff --git a/netty-proxy-client/src/main/resources/application.yml b/netty-proxy-client/src/main/resources/application.yml
new file mode 100644
index 0000000..b374691
--- /dev/null
+++ b/netty-proxy-client/src/main/resources/application.yml
@@ -0,0 +1,2 @@
+server:
+ port: 1003
\ No newline at end of file
diff --git a/netty-proxy-common/pom.xml b/netty-proxy-common/pom.xml
new file mode 100644
index 0000000..5e9a2e9
--- /dev/null
+++ b/netty-proxy-common/pom.xml
@@ -0,0 +1,26 @@
+
+
+
+ netty-proxy
+ com.wu
+ 0.0.1-SNAPSHOT
+
+ 4.0.0
+
+ netty-proxy-common
+
+
+ 8
+ 8
+
+
+
+ io.netty
+ netty-all
+ 4.1.74.Final
+
+
+
+
\ No newline at end of file
diff --git a/netty-proxy-common/src/main/java/com/luck/msg/MyMsg.java b/netty-proxy-common/src/main/java/com/luck/msg/MyMsg.java
new file mode 100644
index 0000000..c9d1087
--- /dev/null
+++ b/netty-proxy-common/src/main/java/com/luck/msg/MyMsg.java
@@ -0,0 +1,58 @@
+package com.luck.msg;
+
+import java.util.Arrays;
+
+public class MyMsg {
+
+ /**
+ * 心跳
+ */
+ public static final byte TYPE_HEARTBEAT = 0X00;
+
+ /**
+ * 连接成功
+ */
+ public static final byte TYPE_CONNECT = 0X01;
+
+ /**
+ * 数据传输
+ */
+ public static final byte TYPE_TRANSFER = 0X02;
+
+ /**
+ * 连接断开
+ */
+ public static final byte TYPE_DISCONNECT = 0X09;
+
+ /**
+ * 数据类型
+ */
+ private byte type;
+
+ /**
+ * 消息传输数据
+ */
+ private byte[] data;
+
+ public byte getType() {
+ return type;
+ }
+
+ public void setType(byte type) {
+ this.type = type;
+ }
+
+ public byte[] getData() {
+ return data;
+ }
+
+ public void setData(byte[] data) {
+ this.data = data;
+ }
+
+ @Override
+ public String toString() {
+ return "MyMsg [type=" + type + ", data=" + Arrays.toString(data) + "]";
+ }
+
+}
\ No newline at end of file
diff --git a/netty-proxy-common/src/main/java/com/luck/msg/MyMsgDecoder.java b/netty-proxy-common/src/main/java/com/luck/msg/MyMsgDecoder.java
new file mode 100644
index 0000000..6f9809e
--- /dev/null
+++ b/netty-proxy-common/src/main/java/com/luck/msg/MyMsgDecoder.java
@@ -0,0 +1,41 @@
+package com.luck.msg;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+
+public class MyMsgDecoder extends LengthFieldBasedFrameDecoder {
+
+ public MyMsgDecoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength, int lengthAdjustment,
+ int initialBytesToStrip) {
+ super(maxFrameLength, lengthFieldOffset, lengthFieldLength, lengthAdjustment, initialBytesToStrip);
+ }
+
+ public MyMsgDecoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength, int lengthAdjustment,
+ int initialBytesToStrip, boolean failFast) {
+ super(maxFrameLength, lengthFieldOffset, lengthFieldLength, lengthAdjustment, initialBytesToStrip, failFast);
+ }
+
+ @Override
+ protected MyMsg decode(ChannelHandlerContext ctx, ByteBuf in2) throws Exception {
+ ByteBuf in = (ByteBuf) super.decode(ctx, in2);
+ if (in == null) {
+ return null;
+ }
+
+ if (in.readableBytes() < 4) {
+ return null;
+ }
+
+ MyMsg myMsg = new MyMsg();
+ int dataLength = in.readInt();
+ byte type = in.readByte();
+ myMsg.setType(type);
+ byte[] data = new byte[dataLength - 5];
+ in.readBytes(data);
+ myMsg.setData(data);
+ in.release();
+
+ return myMsg;
+ }
+}
\ No newline at end of file
diff --git a/netty-proxy-common/src/main/java/com/luck/msg/MyMsgEncoder.java b/netty-proxy-common/src/main/java/com/luck/msg/MyMsgEncoder.java
new file mode 100644
index 0000000..ec10ecf
--- /dev/null
+++ b/netty-proxy-common/src/main/java/com/luck/msg/MyMsgEncoder.java
@@ -0,0 +1,28 @@
+package com.luck.msg;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.MessageToByteEncoder;
+
+public class MyMsgEncoder extends MessageToByteEncoder {
+
+ public MyMsgEncoder() {
+
+ }
+
+ @Override
+ protected void encode(ChannelHandlerContext ctx, MyMsg msg, ByteBuf out) throws Exception {
+ int bodyLength = 5;
+ if (msg.getData() != null) {
+ bodyLength += msg.getData().length;
+ }
+
+ out.writeInt(bodyLength);
+
+ out.writeByte(msg.getType());
+
+ if (msg.getData() != null) {
+ out.writeBytes(msg.getData());
+ }
+ }
+}
\ No newline at end of file
diff --git a/netty-proxy-server/pom.xml b/netty-proxy-server/pom.xml
new file mode 100644
index 0000000..fad37ad
--- /dev/null
+++ b/netty-proxy-server/pom.xml
@@ -0,0 +1,40 @@
+
+
+
+ netty-proxy
+ com.wu
+ 0.0.1-SNAPSHOT
+
+ 4.0.0
+
+ netty-proxy-server
+
+
+ 8
+ 8
+
+
+
+
+ com.wu
+ netty-proxy-common
+ 0.0.1-SNAPSHOT
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ io.springfox
+ springfox-boot-starter
+ 3.0.0
+
+
+ top.wu2020
+ wu-framework-web
+ 1.1.9-JDK1.8-SNAPSHOT
+
+
+
\ No newline at end of file
diff --git a/netty-proxy-server/src/main/java/com/luck/server/ClientHandler.java b/netty-proxy-server/src/main/java/com/luck/server/ClientHandler.java
new file mode 100644
index 0000000..0bbb5f0
--- /dev/null
+++ b/netty-proxy-server/src/main/java/com/luck/server/ClientHandler.java
@@ -0,0 +1,113 @@
+package com.luck.server;
+
+import com.luck.msg.MyMsg;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.*;
+import io.netty.handler.timeout.IdleStateEvent;
+import io.netty.util.internal.StringUtil;
+
+public class ClientHandler extends SimpleChannelInboundHandler {
+
+ @Override
+ public void channelRead0(ChannelHandlerContext ctx, MyMsg myMsg) {
+ // 代理服务器读到客户端数据了
+ byte type = myMsg.getType();
+ switch (type) {
+ case MyMsg.TYPE_HEARTBEAT:
+ MyMsg hb = new MyMsg();
+ hb.setType(MyMsg.TYPE_HEARTBEAT);
+ ctx.channel().writeAndFlush(hb);
+ break;
+ case MyMsg.TYPE_CONNECT:
+ String vid = new String(myMsg.getData());
+ if (StringUtil.isNullOrEmpty(vid) || "client".equals(vid)) {
+ Constant.clientChannel = ctx.channel();
+ } else {
+ // 绑定访客和客户端的连接
+ Channel visitorChannel = Constant.vvc.get(vid);
+ if (null != visitorChannel) {
+ ctx.channel().attr(Constant.VID).set(vid);
+ Constant.vcc.put(vid, ctx.channel());
+
+ // 通道绑定完成可以读取访客数据
+ visitorChannel.config().setOption(ChannelOption.AUTO_READ, true);
+ }
+ }
+ break;
+ case MyMsg.TYPE_DISCONNECT:
+ String disVid = new String(myMsg.getData());
+ Constant.clearVccVvcAndClose(disVid);
+ break;
+ case MyMsg.TYPE_TRANSFER:
+ // 把数据转到用户服务
+ ByteBuf buf = ctx.alloc().buffer(myMsg.getData().length);
+ buf.writeBytes(myMsg.getData());
+
+ String visitorId = ctx.channel().attr(Constant.VID).get();
+ Channel vchannel = Constant.vvc.get(visitorId);
+ if (null != vchannel) {
+ vchannel.writeAndFlush(buf);
+ }
+ break;
+ default:
+ // 操作有误
+ }
+ // 代理服务器发送数据到用户了
+ }
+
+ @Override
+ public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
+ String vid = ctx.channel().attr(Constant.VID).get();
+ if (StringUtil.isNullOrEmpty(vid)) {
+ super.channelWritabilityChanged(ctx);
+ return;
+ }
+ Channel visitorChannel = Constant.vvc.get(vid);
+ if (visitorChannel != null) {
+ visitorChannel.config().setOption(ChannelOption.AUTO_READ, ctx.channel().isWritable());
+ }
+
+ super.channelWritabilityChanged(ctx);
+ }
+
+ @Override
+ public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+ String vid = ctx.channel().attr(Constant.VID).get();
+ if (StringUtil.isNullOrEmpty(vid)) {
+ super.channelInactive(ctx);
+ return;
+ }
+ Channel visitorChannel = Constant.vvc.get(vid);
+ if (visitorChannel != null && visitorChannel.isActive()) {
+ // 数据发送完成后再关闭连接,解决http1.0数据传输问题
+ visitorChannel.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
+ visitorChannel.close();
+ } else {
+ ctx.channel().close();
+ }
+ Constant.clearVccVvc(vid);
+ super.channelInactive(ctx);
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+ super.exceptionCaught(ctx, cause);
+ }
+
+ @Override
+ public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
+ if (evt instanceof IdleStateEvent) {
+ IdleStateEvent event = (IdleStateEvent) evt;
+ switch (event.state()) {
+ case READER_IDLE:
+ ctx.channel().close();
+ break;
+ case WRITER_IDLE:
+ break;
+ case ALL_IDLE:
+ break;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/netty-proxy-server/src/main/java/com/luck/server/Constant.java b/netty-proxy-server/src/main/java/com/luck/server/Constant.java
new file mode 100644
index 0000000..ed6cde3
--- /dev/null
+++ b/netty-proxy-server/src/main/java/com/luck/server/Constant.java
@@ -0,0 +1,82 @@
+package com.luck.server;
+
+import io.netty.channel.Channel;
+import io.netty.util.AttributeKey;
+import io.netty.util.internal.StringUtil;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class Constant {
+ /**
+ * 绑定channel_id
+ */
+ public static final AttributeKey VID = AttributeKey.newInstance("vid");
+ /**
+ * 客户端服务channel
+ */
+ public static Channel clientChannel = null;
+ /**
+ * 访客,客户服务channel
+ */
+ public static Map vcc = new ConcurrentHashMap<>();
+
+ /**
+ * 访客,访客服务channel
+ */
+ public static Map vvc = new ConcurrentHashMap<>();
+
+ /**
+ * 服务代理端口
+ */
+ public static int visitorPort = 16002;
+
+ /**
+ * 服务端口
+ */
+ public static int serverPort = 16001;
+
+ /**
+ * 清除连接
+ *
+ * @param vid 访客ID
+ */
+ public static void clearVccVvc(String vid) {
+ if (StringUtil.isNullOrEmpty(vid)) {
+ return;
+ }
+ Channel clientChannel = vcc.get(vid);
+ if (null != clientChannel) {
+ clientChannel.attr(VID).set(null);
+ vcc.remove(vid);
+ }
+ Channel visitorChannel = vvc.get(vid);
+ if (null != visitorChannel) {
+ visitorChannel.attr(VID).set(null);
+ vvc.remove(vid);
+ }
+ }
+
+ /**
+ * 清除关闭连接
+ *
+ * @param vid 访客ID
+ */
+ public static void clearVccVvcAndClose(String vid) {
+ if (StringUtil.isNullOrEmpty(vid)) {
+ return;
+ }
+ Channel clientChannel = vcc.get(vid);
+ if (null != clientChannel) {
+ clientChannel.attr(VID).set(null);
+ vcc.remove(vid);
+ clientChannel.close();
+ }
+ Channel visitorChannel = vvc.get(vid);
+ if (null != visitorChannel) {
+ visitorChannel.attr(VID).set(null);
+ vvc.remove(vid);
+ visitorChannel.close();
+ }
+ }
+}
\ No newline at end of file
diff --git a/netty-proxy-server/src/main/java/com/luck/server/ServerSocket.java b/netty-proxy-server/src/main/java/com/luck/server/ServerSocket.java
new file mode 100644
index 0000000..4efca69
--- /dev/null
+++ b/netty-proxy-server/src/main/java/com/luck/server/ServerSocket.java
@@ -0,0 +1,61 @@
+package com.luck.server;
+
+import com.luck.msg.MyMsgDecoder;
+import com.luck.msg.MyMsgEncoder;
+import io.netty.bootstrap.ServerBootstrap;
+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.handler.timeout.IdleStateHandler;
+
+public class ServerSocket {
+ private static EventLoopGroup bossGroup = new NioEventLoopGroup();
+ private static EventLoopGroup workerGroup = new NioEventLoopGroup();
+ private static ChannelFuture channelFuture;
+
+ /**
+ * 启动服务端
+ *
+ * @throws Exception
+ */
+ public static void startServer() throws Exception {
+ try {
+
+ ServerBootstrap b = new ServerBootstrap();
+ b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
+ .childHandler(new ChannelInitializer() {
+ @Override
+ public void initChannel(SocketChannel ch) throws Exception {
+ ChannelPipeline pipeline = ch.pipeline();
+ pipeline.addLast(new MyMsgDecoder(Integer.MAX_VALUE, 0, 4, -4, 0));
+ pipeline.addLast(new MyMsgEncoder());
+ pipeline.addLast(new IdleStateHandler(40, 10, 0));
+ pipeline.addLast(new ClientHandler());
+ }
+
+ });
+ channelFuture = b.bind(Constant.serverPort).sync();
+
+ channelFuture.addListener((ChannelFutureListener) channelFuture -> {
+ // 服务器已启动
+ });
+ channelFuture.channel().closeFuture().sync();
+ } finally {
+ shutdown();
+ // 服务器已关闭
+ }
+ }
+
+ public static void shutdown() {
+ if (channelFuture != null) {
+ channelFuture.channel().close().syncUninterruptibly();
+ }
+ if ((bossGroup != null) && (!bossGroup.isShutdown())) {
+ bossGroup.shutdownGracefully();
+ }
+ if ((workerGroup != null) && (!workerGroup.isShutdown())) {
+ workerGroup.shutdownGracefully();
+ }
+ }
+}
\ No newline at end of file
diff --git a/netty-proxy-server/src/main/java/com/luck/server/ServerStart.java b/netty-proxy-server/src/main/java/com/luck/server/ServerStart.java
new file mode 100644
index 0000000..a603eea
--- /dev/null
+++ b/netty-proxy-server/src/main/java/com/luck/server/ServerStart.java
@@ -0,0 +1,17 @@
+package com.luck.server;
+
+
+public class ServerStart {
+ public static void main(String[] args) throws Exception {
+ if (null != args && args.length == 2) {
+ int visitorPort = Integer.parseInt(args[1]);
+ int serverPort = Integer.parseInt(args[0]);
+ Constant.visitorPort = visitorPort;
+ Constant.serverPort = serverPort;
+ }
+ // 启动访客服务端,用于接收访客请求
+ VisitorSocket.startServer();
+ // 启动代理服务端,用于接收客户端请求
+ ServerSocket.startServer();
+ }
+}
\ No newline at end of file
diff --git a/netty-proxy-server/src/main/java/com/luck/server/VisitorHandler.java b/netty-proxy-server/src/main/java/com/luck/server/VisitorHandler.java
new file mode 100644
index 0000000..f42517f
--- /dev/null
+++ b/netty-proxy-server/src/main/java/com/luck/server/VisitorHandler.java
@@ -0,0 +1,103 @@
+package com.luck.server;
+
+import com.luck.msg.MyMsg;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.SimpleChannelInboundHandler;
+import io.netty.util.internal.StringUtil;
+
+import java.util.UUID;
+
+/**
+ * description: 访客处理程序
+ *
+ * @author 吴佳伟
+ * @date: 12.2.23 10:21
+ */
+public class VisitorHandler extends SimpleChannelInboundHandler {
+
+ @Override
+ public void channelActive(ChannelHandlerContext ctx) throws Exception {
+ // 访客连接上代理服务器了
+ Channel visitorChannel = ctx.channel();
+ // 先不读取访客数据
+ visitorChannel.config().setOption(ChannelOption.AUTO_READ, false);
+
+ // 生成访客ID
+ String vid = UUID.randomUUID().toString();
+
+ // 绑定访客通道
+ visitorChannel.attr(Constant.VID).set(vid);
+ Constant.vvc.put(vid, visitorChannel);
+
+ MyMsg myMsg = new MyMsg();
+ myMsg.setType(MyMsg.TYPE_CONNECT);
+ myMsg.setData(vid.getBytes());
+ Constant.clientChannel.writeAndFlush(myMsg);
+
+ super.channelActive(ctx);
+ }
+
+ @Override
+ public void channelRead0(ChannelHandlerContext ctx, ByteBuf buf) {
+ String vid = ctx.channel().attr(Constant.VID).get();
+ if (StringUtil.isNullOrEmpty(vid)) {
+ return;
+ }
+ byte[] bytes = new byte[buf.readableBytes()];
+ buf.readBytes(bytes);
+ MyMsg myMsg = new MyMsg();
+ myMsg.setType(MyMsg.TYPE_TRANSFER);
+ myMsg.setData(bytes);
+
+ // 代理服务器发送数据到客户端了
+ Channel clientChannel = Constant.vcc.get(vid);
+ clientChannel.writeAndFlush(myMsg);
+ }
+
+ @Override
+ public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+ String vid = ctx.channel().attr(Constant.VID).get();
+ if (StringUtil.isNullOrEmpty(vid)) {
+ super.channelInactive(ctx);
+ return;
+ }
+ Channel clientChannel = Constant.vcc.get(vid);
+ if (clientChannel != null && clientChannel.isActive()) {
+
+ clientChannel.config().setOption(ChannelOption.AUTO_READ, true);
+
+ // 通知客户端,访客连接已经断开
+ MyMsg myMsg = new MyMsg();
+ myMsg.setType(MyMsg.TYPE_DISCONNECT);
+ myMsg.setData(vid.getBytes());
+ clientChannel.writeAndFlush(myMsg);
+ }
+ Constant.clearVccVvc(vid);
+ super.channelInactive(ctx);
+ }
+
+ @Override
+ public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
+
+ Channel visitorChannel = ctx.channel();
+ String vid = visitorChannel.attr(Constant.VID).get();
+ if (StringUtil.isNullOrEmpty(vid)) {
+ super.channelWritabilityChanged(ctx);
+ return;
+ }
+ Channel clientChannel = Constant.vcc.get(vid);
+ if (clientChannel != null) {
+ clientChannel.config().setOption(ChannelOption.AUTO_READ, visitorChannel.isWritable());
+ }
+
+ super.channelWritabilityChanged(ctx);
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+ ctx.close();
+ }
+}
\ No newline at end of file
diff --git a/netty-proxy-server/src/main/java/com/luck/server/VisitorSocket.java b/netty-proxy-server/src/main/java/com/luck/server/VisitorSocket.java
new file mode 100644
index 0000000..2affe79
--- /dev/null
+++ b/netty-proxy-server/src/main/java/com/luck/server/VisitorSocket.java
@@ -0,0 +1,37 @@
+package com.luck.server;
+
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.ChannelDuplexHandler;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelPipeline;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+
+public class VisitorSocket {
+ private static EventLoopGroup bossGroup = new NioEventLoopGroup();
+ private static EventLoopGroup workerGroup = new NioEventLoopGroup();
+
+ /**
+ * 启动服务代理
+ *
+ * @throws Exception
+ */
+ public static void startServer() throws Exception {
+
+ ServerBootstrap b = new ServerBootstrap();
+ b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
+ .childHandler(new ChannelInitializer() {
+ @Override
+ public void initChannel(SocketChannel ch) throws Exception {
+ ChannelPipeline pipeline = ch.pipeline();
+ pipeline.addLast(new ChannelDuplexHandler());
+ pipeline.addLast(new VisitorHandler());
+ }
+ });
+ b.bind(Constant.visitorPort).get();
+
+ }
+
+}
\ No newline at end of file
diff --git a/netty-proxy-server/src/main/java/com/luck/server/controller/ServerController.java b/netty-proxy-server/src/main/java/com/luck/server/controller/ServerController.java
new file mode 100644
index 0000000..9e09362
--- /dev/null
+++ b/netty-proxy-server/src/main/java/com/luck/server/controller/ServerController.java
@@ -0,0 +1,17 @@
+package com.luck.server.controller;
+
+import com.wu.framework.inner.layer.web.EasyController;
+import com.wu.framework.response.Result;
+import com.wu.framework.response.ResultFactory;
+import io.swagger.annotations.Api;
+import org.springframework.web.bind.annotation.GetMapping;
+
+@Api(tags = "服务端")
+@EasyController("/server")
+public class ServerController {
+
+ @GetMapping("/version")
+ public Result version() {
+ return ResultFactory.successOf("服务端端版本");
+ }
+}
diff --git a/netty-proxy-server/src/main/resources/application.yml b/netty-proxy-server/src/main/resources/application.yml
new file mode 100644
index 0000000..b374691
--- /dev/null
+++ b/netty-proxy-server/src/main/resources/application.yml
@@ -0,0 +1,2 @@
+server:
+ port: 1003
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..d2d7958
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,68 @@
+
+
+ 4.0.0
+ pom
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 3.0.2
+
+
+ com.wu
+ netty-proxy
+ 0.0.1-SNAPSHOT
+ netty-proxy
+ netty-proxy
+
+ 19
+
+
+
+ netty-proxy-common
+ netty-proxy-server
+ netty-proxy-client
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+ oss.snapshots
+ oss.sonatype.org
+ https://oss.sonatype.org/content/repositories/snapshots/
+
+ false
+
+
+ true
+
+
+
+
+
+
+
+ org.graalvm.buildtools
+ native-maven-plugin
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+