From ded5e2f92701d0c7a9c9de9ea190ea8a9afdf416 Mon Sep 17 00:00:00 2001 From: aoshiguchen <1052045476@qq.com> Date: Sun, 16 Oct 2022 16:45:27 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=96=87=E6=A1=A3=EF=BC=8C?= =?UTF-8?q?=E6=8F=8F=E8=BF=B0=E4=BB=A3=E7=90=86=E4=BB=A3=E7=90=86=E8=BF=9E?= =?UTF-8?q?=E6=8E=A5=E3=80=81=E6=95=B0=E6=8D=AE=E6=B5=81=E5=90=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + docs/Channel.MD | 54 +++++++++++++++++++ .../handler/ProxyMessageConnectHandler.java | 1 + 3 files changed, 56 insertions(+) create mode 100644 docs/Channel.MD diff --git a/README.md b/README.md index f9e663ed..106ee7f3 100644 --- a/README.md +++ b/README.md @@ -106,6 +106,7 @@ proxy: # 7、技术文档 - [Aop](./docs/Aop.MD) +- [Channel](./docs/Channel.MD) # 8、联系我们 - 微信: yuyunshize diff --git a/docs/Channel.MD b/docs/Channel.MD new file mode 100644 index 00000000..b28985cc --- /dev/null +++ b/docs/Channel.MD @@ -0,0 +1,54 @@ +# 代理实现中涉及的几类Channel + +## 指令通道(CmdChannel) +- 该channel负责维护客户端与服务端之间的指令通讯,由客户端启动后向服务端发起连接请求,服务端验证license成功后连接建立。 +- 服务端维护第一个CmdChannel的映射表,key为licenseId。服务端可以根据licenseId,向指定的客户端指令通道发布指令。 +- 该通道建立完成后,服务端会将该license授权的外网映射端口打开,等待用户访问。正常情况下,只要客户端不下线,该通道一直可用。 +- 然后服务端维护第二个CmdChannel的映射表,key为服务端外网端口。服务端可以根据指定的外网端口,向指定的客户端指令通道发布指令。 + +## 用户访问通道(VisitorChannel) +- 该channel负责维护访问者与服务端端口之间的通讯,由访问者向服务端映射的外网端口发起请求开始建立,具体断开时机由实际的被代理的 +协议决定。 +- 如HTTP/1.0下,用户向该端口发起请求,响应结束后,该通道随之关闭,下一次发起请求后,重新建立新的连接。 +- 该连接建立后,服务端会向该外网端口映射的指令通道发送`Connect`指令,传输该外网端口需要代理的内网信息, +如:`127.0.0.1:3306`。 + +## 被代理服务的实际通道(RealServerChannel) +- 该channel负责维护客户端与实际被代理服务之间的通讯,当客户端接收到服务端的`Connect`指令后,客户端便会建立该通道。 + +## 代理数据传输的通道(ProxyChannel) +- 该channel负责完成内网被代理服务与代理服务端之间的数据转发任务。 +- 每个客户端维护一个`ProxyChannel`的缓存队列,需要时从该队列中取,当取不到是,直接新建一个`ProxyChannel`返回。 +当一个`ProxyChannel`实例用完后,需要归还到缓存队列中(`ProxyChannel`收到`DisConnect`指令时)。 +- 当`RealServerChannel`建立完成后,就会获取一个`ProxyChannel`,并绑定对应的`RealServerChannel`,并设置`RealServerChannel`为 +可读状态。然后通过`ProxyChannel`向服务端发送`Connect`指令。 + + +# 代理实现流程 +## 1、服务连接阶段 +- 1.1、客户端根据是否需要使用SSL,选择对应的服务端端口发起连接,建立`CmdChannel`。 +- 1.2、客户端根据用户输入或配置文件获取`license`,并携带`license`通过`CmdChannel`向服务端发送`Auth`指令。 +- 1.3、服务端通过`CmdChannel`接收到来自客户端的`Auth`指令。若验证`license`有效,则建立`licenseId`与`CmdChannel`的映射缓存、 +外网端口与`CmdChannel`的映射缓存。并启动服务端代理端口,等待用户连接。 +## 2、用户连接阶段 +- 2.1、用户访向服务端代理的外网端口发起请求,服务端建立`VisitorChannel`。 +- 2.2、根据外网端口查找`CmdChannel`,若不存在有效的`CmdChannel`,则关闭该`VisitorChannel`。否则, +设置`VisitorChannel`为不可读,并携带内网映射信息(如:`127.0.0.1:3306`)通过`CmdChannel`向客户端发送`Connect`指令。 +## 3、实际被代理服务连接阶段 +- 3.1、客户端通过`CmdChannel`接收到服务端的`Connect`指令。拿到需要代理的内网IP、端口号,向实际被 +代理服务发起连接请求,若连接失败,则通过`CmdChannel`向服务端发送`DisConnect`指令。建立`RealServerChannel`成功,设置`RealServerChannel`为不可读 +状态,并进入4.1阶段 +## 4、代理通道连接阶段 +- 4.1、客户端通过`ProxyChannelQueue`获取或新建一个`ProxyChannel`,并将`RealServerChannel`与`ProxyChannel`进行绑定。 +- 4.2、客户端通过`ProxyChannel`向服务端发送`Connect`指令。 +- 4.3、服务端通过`ProxyChannel`通道收到来自客户端的`Connect`指令后,将`ProxyChannel`与对应的`VisitorChannel`进行绑定,并 +设置`VisitorChannel`为可读状态。 +## 5、数据传输阶段 +- 5.1、服务端通过`VisitorChannel`收到来自用户的请求数据,然后找到`VisitorChannel`绑定的`ProxyChannel`, +通过`ProxyChannel`发送`Transfer`指令,并携带用户请求数据。 +- 5.2、客户端通过`ProxyChannel`收到来自服务端的`Transfer`指令,取出用户请求数据。找到`ProxyChannel`绑定的`RealServerChannel`, +通过`RealServerChannel`向被代理服务写入用户请求数据。 +- 5.3、客户端通过`RealServerChannel`收到被代理服务响应的数据,找到`RealServerChannel`绑定的`ProxyChannel`, +通过`ProxyChannel`向服务端发送`Transfer`指令,并携带响应数据。 +- 5.4、服务端通过`ProxyChannel`收到来自客户端的`Transfer`指令,找到`ProxyChannel`绑定的`VisitorChannel`, +通过`VisitorChannel`向用户端写入响应数据。 diff --git a/neutrino-proxy-client/src/main/java/fun/asgc/neutrino/proxy/client/handler/ProxyMessageConnectHandler.java b/neutrino-proxy-client/src/main/java/fun/asgc/neutrino/proxy/client/handler/ProxyMessageConnectHandler.java index 59a2d983..7cd3e13a 100644 --- a/neutrino-proxy-client/src/main/java/fun/asgc/neutrino/proxy/client/handler/ProxyMessageConnectHandler.java +++ b/neutrino-proxy-client/src/main/java/fun/asgc/neutrino/proxy/client/handler/ProxyMessageConnectHandler.java @@ -54,6 +54,7 @@ public class ProxyMessageConnectHandler implements ProxyMessageHandler { String[] serverInfo = new String(proxyMessage.getData()).split(":"); String ip = serverInfo[0]; int port = Integer.parseInt(serverInfo[1]); + // 连接真实的、被代理的服务 realServerBootstrap.connect(ip, port).addListener(new ChannelFutureListener() { @Override