马大波 преди 1 година
ревизия
1afbc60afc
променени са 18 файла, в които са добавени 643 реда и са изтрити 0 реда
  1. 36 0
      .gitignore
  2. 79 0
      pom.xml
  3. 11 0
      src/main/java/com/xiaobao/gateway/Application.java
  4. 38 0
      src/main/java/com/xiaobao/gateway/protocol/proxy/codec/ProxyInDecoder.java
  5. 38 0
      src/main/java/com/xiaobao/gateway/protocol/proxy/codec/ProxyOutEncoder.java
  6. 78 0
      src/main/java/com/xiaobao/gateway/protocol/proxy/forward/ForwardProxyClientHandler.java
  7. 24 0
      src/main/java/com/xiaobao/gateway/protocol/proxy/forward/ForwardProxyClientInitializer.java
  8. 42 0
      src/main/java/com/xiaobao/gateway/protocol/proxy/forward/ForwardProxyClientToTargetHandler.java
  9. 43 0
      src/main/java/com/xiaobao/gateway/protocol/proxy/forward/ForwardProxyServer.java
  10. 24 0
      src/main/java/com/xiaobao/gateway/protocol/proxy/forward/ForwardProxyServerInitializer.java
  11. 78 0
      src/main/java/com/xiaobao/gateway/protocol/proxy/reverse/ReverseProxyClientHandler.java
  12. 20 0
      src/main/java/com/xiaobao/gateway/protocol/proxy/reverse/ReverseProxyClientInitializer.java
  13. 45 0
      src/main/java/com/xiaobao/gateway/protocol/proxy/reverse/ReverseProxyClientToTargetHandler.java
  14. 43 0
      src/main/java/com/xiaobao/gateway/protocol/proxy/reverse/ReverseProxyServer.java
  15. 28 0
      src/main/java/com/xiaobao/gateway/protocol/proxy/reverse/ReverseProxyServerInitializer.java
  16. 5 0
      src/main/java/com/xiaobao/gateway/protocol/proxy/reverse/ReverseProxyServerOutEncoder.java
  17. 2 0
      src/main/resources/application.yml
  18. 9 0
      src/test/java/com/xiaobao/gateway/ApplicationTests.java

+ 36 - 0
.gitignore

@@ -0,0 +1,36 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+logs/
+storage/
+.DS_Store
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/

+ 79 - 0
pom.xml

@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>2.7.18</version>
+        <relativePath/> <!-- lookup parent from repository -->
+    </parent>
+    <groupId>com.xiaobao.gateway</groupId>
+    <artifactId>tcp-proxy-netty</artifactId>
+    <version>1.0.0</version>
+    <name>tcp-proxy-netty</name>
+    <properties>
+        <java.version>1.8</java.version>
+        <fastjson.version>2.0.52</fastjson.version>
+        <netty.version>4.1.112.Final</netty.version>
+    </properties>
+    <dependencies>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-configuration-processor</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba.fastjson2</groupId>
+            <artifactId>fastjson2</artifactId>
+            <version>${fastjson.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-all</artifactId>
+            <version>${netty.version}</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                    <excludes>
+                        <exclude>
+                            <groupId>org.projectlombok</groupId>
+                            <artifactId>lombok</artifactId>
+                        </exclude>
+                    </excludes>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <version>2.3.2</version>
+                <configuration>
+                    <excludes>
+                        <exclude>**/application.yml</exclude>
+                    </excludes>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 11 - 0
src/main/java/com/xiaobao/gateway/Application.java

@@ -0,0 +1,11 @@
+package com.xiaobao.gateway;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Application {
+    public static void main(String[] args) {
+        SpringApplication.run(Application.class, args);
+    }
+}

+ 38 - 0
src/main/java/com/xiaobao/gateway/protocol/proxy/codec/ProxyInDecoder.java

@@ -0,0 +1,38 @@
+package com.xiaobao.gateway.protocol.proxy.codec;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.ByteToMessageDecoder;
+import io.netty.util.internal.logging.InternalLogger;
+import io.netty.util.internal.logging.InternalLoggerFactory;
+
+import java.util.List;
+
+public class ProxyInDecoder extends ByteToMessageDecoder {
+
+    private static final InternalLogger log = InternalLoggerFactory.getInstance(ProxyInDecoder.class);
+
+    @Override
+    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
+        if (in.readableBytes() < 6) {
+            return;
+        }
+        // 标记读索引位置
+        in.markReaderIndex();
+        // 读取消息长度(4 字节)
+        int len = in.readInt();
+        // 读取消息类型(2 字节)
+        short identifier = in.readShort();
+        log.info("自定义标识: {}", identifier);
+        // 检查是否有足够的字节来读取消息体
+        if (in.readableBytes() < len) {
+            // 重置读索引
+            in.resetReaderIndex();
+            return;
+        }
+        // 读取消息体
+        ByteBuf body = ctx.alloc().buffer(len);
+        in.readBytes(body);
+        out.add(body);
+    }
+}

+ 38 - 0
src/main/java/com/xiaobao/gateway/protocol/proxy/codec/ProxyOutEncoder.java

@@ -0,0 +1,38 @@
+package com.xiaobao.gateway.protocol.proxy.codec;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelOutboundHandlerAdapter;
+import io.netty.channel.ChannelPromise;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class ProxyOutEncoder extends ChannelOutboundHandlerAdapter {
+    /**
+     * 自定义标识
+     */
+    private short identifier;
+
+    public ProxyOutEncoder(short identifier) {
+        this.identifier = identifier;
+    }
+
+    @Override
+    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
+        if (msg instanceof ByteBuf) {
+            ByteBuf out = ctx.alloc().ioBuffer();
+
+            ByteBuf buf = (ByteBuf) msg;
+            int len = buf.readableBytes();
+
+            out.writeInt(len);
+            out.writeShort(identifier);
+            out.writeBytes(buf);
+            ctx.write(out, promise);
+        } else {
+            super.write(ctx, msg, promise);
+        }
+    }
+}

+ 78 - 0
src/main/java/com/xiaobao/gateway/protocol/proxy/forward/ForwardProxyClientHandler.java

@@ -0,0 +1,78 @@
+package com.xiaobao.gateway.protocol.proxy.forward;
+
+import io.netty.bootstrap.Bootstrap;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.*;
+
+public class ForwardProxyClientHandler extends ChannelInboundHandlerAdapter {
+    private final String remoteHost;
+    private final int remotePort;
+
+    // As we use inboundChannel.eventLoop() when building the Bootstrap this does not need to be volatile as
+    // the outboundChannel will use the same EventLoop (and therefore Thread) as the inboundChannel.
+    private Channel outboundChannel;
+
+    public ForwardProxyClientHandler(String remoteHost, int remotePort) {
+        this.remoteHost = remoteHost;
+        this.remotePort = remotePort;
+    }
+
+    @Override
+    public void channelActive(ChannelHandlerContext ctx) {
+        final Channel inboundChannel = ctx.channel();
+
+        // Start the connection attempt.
+        Bootstrap b = new Bootstrap();
+        b.group(inboundChannel.eventLoop())
+            .channel(ctx.channel().getClass())
+            .handler(new ForwardProxyClientInitializer(inboundChannel))
+            .option(ChannelOption.AUTO_READ, false);
+        ChannelFuture f = b.connect(remoteHost, remotePort);
+        outboundChannel = f.channel();
+        f.addListener((ChannelFutureListener) future -> {
+            if (future.isSuccess()) {
+                // connection complete start to read first data
+                inboundChannel.read();
+            } else {
+                // Close the connection if the connection attempt has failed.
+                inboundChannel.close();
+            }
+        });
+    }
+
+    @Override
+    public void channelRead(final ChannelHandlerContext ctx, Object msg) {
+        if (outboundChannel.isActive()) {
+            outboundChannel.writeAndFlush(msg).addListener((ChannelFutureListener) future -> {
+                if (future.isSuccess()) {
+                    // was able to flush out data, start to read the next chunk
+                    ctx.channel().read();
+                } else {
+                    future.channel().close();
+                }
+            });
+        }
+    }
+
+    @Override
+    public void channelInactive(ChannelHandlerContext ctx) {
+        if (outboundChannel != null) {
+            closeOnFlush(outboundChannel);
+        }
+    }
+
+    @Override
+    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
+        cause.printStackTrace();
+        closeOnFlush(ctx.channel());
+    }
+
+    /**
+     * Closes the specified channel after all queued write requests are flushed.
+     */
+    static void closeOnFlush(Channel ch) {
+        if (ch.isActive()) {
+            ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
+        }
+    }
+}

+ 24 - 0
src/main/java/com/xiaobao/gateway/protocol/proxy/forward/ForwardProxyClientInitializer.java

@@ -0,0 +1,24 @@
+package com.xiaobao.gateway.protocol.proxy.forward;
+
+import com.xiaobao.gateway.protocol.proxy.codec.ProxyInDecoder;
+import com.xiaobao.gateway.protocol.proxy.codec.ProxyOutEncoder;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelPipeline;
+import io.netty.channel.socket.SocketChannel;
+
+public class ForwardProxyClientInitializer extends ChannelInitializer<SocketChannel> {
+    private final Channel inboundChannel;
+
+    public ForwardProxyClientInitializer(Channel inboundChannel) {
+        this.inboundChannel = inboundChannel;
+    }
+
+    @Override
+    protected void initChannel(SocketChannel ch) throws Exception {
+        ChannelPipeline pipeline = ch.pipeline();
+        pipeline.addLast(new ProxyOutEncoder((short) 1234));
+        pipeline.addLast(new ProxyInDecoder());
+        pipeline.addLast(new ForwardProxyClientToTargetHandler(inboundChannel));
+    }
+}

+ 42 - 0
src/main/java/com/xiaobao/gateway/protocol/proxy/forward/ForwardProxyClientToTargetHandler.java

@@ -0,0 +1,42 @@
+package com.xiaobao.gateway.protocol.proxy.forward;
+
+import io.netty.channel.*;
+
+public class ForwardProxyClientToTargetHandler extends ChannelInboundHandlerAdapter {
+    private final Channel inboundChannel;
+
+    public ForwardProxyClientToTargetHandler(Channel inboundChannel) {
+        this.inboundChannel = inboundChannel;
+    }
+
+    @Override
+    public void channelActive(ChannelHandlerContext ctx) {
+        if (!inboundChannel.isActive()) {
+            ForwardProxyClientHandler.closeOnFlush(ctx.channel());
+        } else {
+            ctx.read();
+        }
+    }
+
+    @Override
+    public void channelRead(final ChannelHandlerContext ctx, Object msg) {
+        inboundChannel.writeAndFlush(msg).addListener((ChannelFutureListener) future -> {
+            if (future.isSuccess()) {
+                ctx.channel().read();
+            } else {
+                future.channel().close();
+            }
+        });
+    }
+
+    @Override
+    public void channelInactive(ChannelHandlerContext ctx) {
+        ForwardProxyClientHandler.closeOnFlush(inboundChannel);
+    }
+
+    @Override
+    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
+        cause.printStackTrace();
+        ForwardProxyClientHandler.closeOnFlush(ctx.channel());
+    }
+}

+ 43 - 0
src/main/java/com/xiaobao/gateway/protocol/proxy/forward/ForwardProxyServer.java

@@ -0,0 +1,43 @@
+package com.xiaobao.gateway.protocol.proxy.forward;
+
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import io.netty.handler.logging.LogLevel;
+import io.netty.handler.logging.LoggingHandler;
+import io.netty.util.internal.logging.InternalLogger;
+import io.netty.util.internal.logging.InternalLoggerFactory;
+
+/**
+ * 正向代理(代理客户端)
+ */
+public class ForwardProxyServer {
+
+    private static final InternalLogger log = InternalLoggerFactory.getInstance(ForwardProxyServer.class);
+
+    private static final int LOCAL_PORT = 1935;
+    private static final String REMOTE_HOST = "127.0.0.1";
+    private static final int REMOTE_PORT = 1936;
+
+    public static void main(String[] args) throws Exception {
+        log.info("Forward Proxying *:{} to {}:{}", LOCAL_PORT, REMOTE_HOST, REMOTE_PORT);
+
+        // Configure the bootstrap.
+        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
+        EventLoopGroup workerGroup = new NioEventLoopGroup();
+        try {
+            ServerBootstrap b = new ServerBootstrap();
+            b.group(bossGroup, workerGroup)
+                .channel(NioServerSocketChannel.class)
+                .handler(new LoggingHandler(LogLevel.INFO))
+                .childHandler(new ForwardProxyServerInitializer(REMOTE_HOST, REMOTE_PORT))
+                .childOption(ChannelOption.AUTO_READ, false)
+                .bind(LOCAL_PORT).sync().channel().closeFuture().sync();
+        } finally {
+            bossGroup.shutdownGracefully();
+            workerGroup.shutdownGracefully();
+        }
+    }
+}

+ 24 - 0
src/main/java/com/xiaobao/gateway/protocol/proxy/forward/ForwardProxyServerInitializer.java

@@ -0,0 +1,24 @@
+package com.xiaobao.gateway.protocol.proxy.forward;
+
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelPipeline;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.handler.logging.LogLevel;
+import io.netty.handler.logging.LoggingHandler;
+
+public class ForwardProxyServerInitializer extends ChannelInitializer<SocketChannel> {
+    private final String remoteHost;
+    private final int remotePort;
+
+    public ForwardProxyServerInitializer(String remoteHost, int remotePort) {
+        this.remoteHost = remoteHost;
+        this.remotePort = remotePort;
+    }
+
+    @Override
+    public void initChannel(SocketChannel ch) {
+        ChannelPipeline pipeline = ch.pipeline();
+        pipeline.addLast(new LoggingHandler(LogLevel.INFO));
+        pipeline.addLast(new ForwardProxyClientHandler(remoteHost, remotePort));
+    }
+}

+ 78 - 0
src/main/java/com/xiaobao/gateway/protocol/proxy/reverse/ReverseProxyClientHandler.java

@@ -0,0 +1,78 @@
+package com.xiaobao.gateway.protocol.proxy.reverse;
+
+import io.netty.bootstrap.Bootstrap;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.*;
+
+public class ReverseProxyClientHandler extends ChannelInboundHandlerAdapter {
+    private final String remoteHost;
+    private final int remotePort;
+
+    // As we use inboundChannel.eventLoop() when building the Bootstrap this does not need to be volatile as
+    // the outboundChannel will use the same EventLoop (and therefore Thread) as the inboundChannel.
+    private Channel outboundChannel;
+
+    public ReverseProxyClientHandler(String remoteHost, int remotePort) {
+        this.remoteHost = remoteHost;
+        this.remotePort = remotePort;
+    }
+
+    @Override
+    public void channelActive(ChannelHandlerContext ctx) {
+        final Channel inboundChannel = ctx.channel();
+
+        // Start the connection attempt.
+        Bootstrap b = new Bootstrap();
+        b.group(inboundChannel.eventLoop())
+            .channel(ctx.channel().getClass())
+            .handler(new ReverseProxyClientInitializer(inboundChannel))
+            .option(ChannelOption.AUTO_READ, false);
+        ChannelFuture f = b.connect(remoteHost, remotePort);
+        outboundChannel = f.channel();
+        f.addListener((ChannelFutureListener) future -> {
+            if (future.isSuccess()) {
+                // connection complete start to read first data
+                inboundChannel.read();
+            } else {
+                // Close the connection if the connection attempt has failed.
+                inboundChannel.close();
+            }
+        });
+    }
+
+    @Override
+    public void channelRead(final ChannelHandlerContext ctx, Object msg) {
+        if (outboundChannel.isActive()) {
+            outboundChannel.writeAndFlush(msg).addListener((ChannelFutureListener) future -> {
+                if (future.isSuccess()) {
+                    // was able to flush out data, start to read the next chunk
+                    ctx.channel().read();
+                } else {
+                    future.channel().close();
+                }
+            });
+        }
+    }
+
+    @Override
+    public void channelInactive(ChannelHandlerContext ctx) {
+        if (outboundChannel != null) {
+            closeOnFlush(outboundChannel);
+        }
+    }
+
+    @Override
+    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
+        cause.printStackTrace();
+        closeOnFlush(ctx.channel());
+    }
+
+    /**
+     * Closes the specified channel after all queued write requests are flushed.
+     */
+    static void closeOnFlush(Channel ch) {
+        if (ch.isActive()) {
+            ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
+        }
+    }
+}

+ 20 - 0
src/main/java/com/xiaobao/gateway/protocol/proxy/reverse/ReverseProxyClientInitializer.java

@@ -0,0 +1,20 @@
+package com.xiaobao.gateway.protocol.proxy.reverse;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelPipeline;
+import io.netty.channel.socket.SocketChannel;
+
+public class ReverseProxyClientInitializer extends ChannelInitializer<SocketChannel> {
+    private final Channel inboundChannel;
+
+    public ReverseProxyClientInitializer(Channel inboundChannel) {
+        this.inboundChannel = inboundChannel;
+    }
+
+    @Override
+    protected void initChannel(SocketChannel ch) throws Exception {
+        ChannelPipeline pipeline = ch.pipeline();
+        pipeline.addLast(new ReverseProxyClientToTargetHandler(inboundChannel));
+    }
+}

+ 45 - 0
src/main/java/com/xiaobao/gateway/protocol/proxy/reverse/ReverseProxyClientToTargetHandler.java

@@ -0,0 +1,45 @@
+package com.xiaobao.gateway.protocol.proxy.reverse;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFutureListener;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+
+public class ReverseProxyClientToTargetHandler extends ChannelInboundHandlerAdapter {
+    private final Channel inboundChannel;
+
+    public ReverseProxyClientToTargetHandler(Channel inboundChannel) {
+        this.inboundChannel = inboundChannel;
+    }
+
+    @Override
+    public void channelActive(ChannelHandlerContext ctx) {
+        if (!inboundChannel.isActive()) {
+            ReverseProxyClientHandler.closeOnFlush(ctx.channel());
+        } else {
+            ctx.read();
+        }
+    }
+
+    @Override
+    public void channelRead(final ChannelHandlerContext ctx, Object msg) {
+        inboundChannel.writeAndFlush(msg).addListener((ChannelFutureListener) future -> {
+            if (future.isSuccess()) {
+                ctx.channel().read();
+            } else {
+                future.channel().close();
+            }
+        });
+    }
+
+    @Override
+    public void channelInactive(ChannelHandlerContext ctx) {
+        ReverseProxyClientHandler.closeOnFlush(inboundChannel);
+    }
+
+    @Override
+    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
+        cause.printStackTrace();
+        ReverseProxyClientHandler.closeOnFlush(ctx.channel());
+    }
+}

+ 43 - 0
src/main/java/com/xiaobao/gateway/protocol/proxy/reverse/ReverseProxyServer.java

@@ -0,0 +1,43 @@
+package com.xiaobao.gateway.protocol.proxy.reverse;
+
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import io.netty.handler.logging.LogLevel;
+import io.netty.handler.logging.LoggingHandler;
+import io.netty.util.internal.logging.InternalLogger;
+import io.netty.util.internal.logging.InternalLoggerFactory;
+
+/**
+ * 反向代理(代理服务端)
+ */
+public class ReverseProxyServer {
+
+    private static final InternalLogger log = InternalLoggerFactory.getInstance(ReverseProxyServer.class);
+
+    private static final int LOCAL_PORT = 1936;
+    private static final String REMOTE_HOST = "192.168.66.73";
+    private static final int REMOTE_PORT = 1935;
+
+    public static void main(String[] args) throws Exception {
+        log.info("Reverse Proxying *:{} to {}:{}", LOCAL_PORT, REMOTE_HOST, REMOTE_PORT);
+
+        // Configure the bootstrap.
+        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
+        EventLoopGroup workerGroup = new NioEventLoopGroup();
+        try {
+            ServerBootstrap b = new ServerBootstrap();
+            b.group(bossGroup, workerGroup)
+                .channel(NioServerSocketChannel.class)
+                .handler(new LoggingHandler(LogLevel.INFO))
+                .childHandler(new ReverseProxyServerInitializer(REMOTE_HOST, REMOTE_PORT))
+                .childOption(ChannelOption.AUTO_READ, false)
+                .bind(LOCAL_PORT).sync().channel().closeFuture().sync();
+        } finally {
+            bossGroup.shutdownGracefully();
+            workerGroup.shutdownGracefully();
+        }
+    }
+}

+ 28 - 0
src/main/java/com/xiaobao/gateway/protocol/proxy/reverse/ReverseProxyServerInitializer.java

@@ -0,0 +1,28 @@
+package com.xiaobao.gateway.protocol.proxy.reverse;
+
+import com.xiaobao.gateway.protocol.proxy.codec.ProxyInDecoder;
+import com.xiaobao.gateway.protocol.proxy.codec.ProxyOutEncoder;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelPipeline;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.handler.logging.LogLevel;
+import io.netty.handler.logging.LoggingHandler;
+
+public class ReverseProxyServerInitializer extends ChannelInitializer<SocketChannel> {
+    private final String remoteHost;
+    private final int remotePort;
+
+    public ReverseProxyServerInitializer(String remoteHost, int remotePort) {
+        this.remoteHost = remoteHost;
+        this.remotePort = remotePort;
+    }
+
+    @Override
+    public void initChannel(SocketChannel ch) {
+        ChannelPipeline pipeline = ch.pipeline();
+        pipeline.addLast(new ProxyOutEncoder((short) 5678));
+        pipeline.addLast(new ProxyInDecoder());
+        pipeline.addLast(new LoggingHandler(LogLevel.INFO));
+        pipeline.addLast(new ReverseProxyClientHandler(remoteHost, remotePort));
+    }
+}

+ 5 - 0
src/main/java/com/xiaobao/gateway/protocol/proxy/reverse/ReverseProxyServerOutEncoder.java

@@ -0,0 +1,5 @@
+package com.xiaobao.gateway.protocol.proxy.reverse;
+
+public class ReverseProxyServerOutEncoder
+{
+}

+ 2 - 0
src/main/resources/application.yml

@@ -0,0 +1,2 @@
+server:
+  port: 8000

+ 9 - 0
src/test/java/com/xiaobao/gateway/ApplicationTests.java

@@ -0,0 +1,9 @@
+package com.xiaobao.gateway;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@Slf4j
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+class ApplicationTests {
+}