├── .gitignore
├── client
├── src
│ └── main
│ │ ├── resources
│ │ ├── config.properties
│ │ ├── logback-test.xml
│ │ └── logback.xml
│ │ └── java
│ │ └── com
│ │ └── fys
│ │ ├── handler
│ │ ├── LoginFailHandler.java
│ │ ├── LoginHandler.java
│ │ ├── ServerStartSuccessHandler.java
│ │ ├── ServerStartFailHandler.java
│ │ ├── DataConnectionHandler.java
│ │ └── CmdDecoder.java
│ │ ├── Config.java
│ │ ├── AppClient.java
│ │ └── DataConnectionClient.java
└── pom.xml
├── server
├── src
│ ├── main
│ │ ├── resources
│ │ │ ├── test.toml
│ │ │ ├── logback-test.xml
│ │ │ ├── config.json
│ │ │ └── logback.xml
│ │ └── java
│ │ │ └── com
│ │ │ └── fys
│ │ │ ├── conf
│ │ │ ├── ServerInfo.java
│ │ │ └── ServerWorker.java
│ │ │ ├── Config.java
│ │ │ ├── handler
│ │ │ ├── LoginCmdHandler.java
│ │ │ ├── ServerCmdDecoder.java
│ │ │ └── FlowManagerHandler.java
│ │ │ ├── App.java
│ │ │ ├── ServerManager.java
│ │ │ └── Server.java
│ └── test
│ │ └── java
│ │ └── com
│ │ └── fys
│ │ ├── H2Test.java
│ │ └── ConfigTest.java
└── pom.xml
├── common
├── src
│ ├── main
│ │ └── java
│ │ │ └── com
│ │ │ └── fys
│ │ │ └── cmd
│ │ │ ├── exception
│ │ │ └── AuthenticationException.java
│ │ │ ├── message
│ │ │ ├── Ping.java
│ │ │ ├── Pong.java
│ │ │ ├── Cmd.java
│ │ │ ├── serverToClient
│ │ │ │ ├── LoginFailCmd.java
│ │ │ │ ├── ServerStartSuccessCmd.java
│ │ │ │ └── ServerStartFailCmd.java
│ │ │ ├── clientToServer
│ │ │ │ └── LoginCmd.java
│ │ │ └── DataConnectionCmd.java
│ │ │ ├── handler
│ │ │ ├── CmdEncoder.java
│ │ │ ├── TimeOutHandler.java
│ │ │ ├── ExceptionHandler.java
│ │ │ ├── TransactionHandler.java
│ │ │ ├── PingPongHandler.java
│ │ │ └── Rc4Md5Handler.java
│ │ │ ├── listener
│ │ │ └── ErrorLogListener.java
│ │ │ └── util
│ │ │ └── CodeUtil.java
│ └── test
│ │ └── java
│ │ └── com
│ │ └── fys
│ │ └── cmd
│ │ ├── message
│ │ ├── clientToServer
│ │ │ └── LoginCmdTest.java
│ │ ├── serverToClient
│ │ │ ├── LoginFailCmdTest.java
│ │ │ ├── ServerStartSuccessCmdTest.java
│ │ │ └── ServerStartFailCmdTest.java
│ │ └── DataConnectionCmdTest.java
│ │ └── handler
│ │ └── Rc4Md5HandlerTest.java
└── pom.xml
├── README.md
└── pom.xml
/.gitignore:
--------------------------------------------------------------------------------
1 | target
2 | .classpath
3 | *~
4 | *.iml
5 | .idea
6 | *.log
7 | *dependency-reduced-pom.xml
8 |
--------------------------------------------------------------------------------
/client/src/main/resources/config.properties:
--------------------------------------------------------------------------------
1 | serverIp=127.0.0.1
2 | serverPort=9050
3 | clientName=hcy_home_pc
4 | token=123456
--------------------------------------------------------------------------------
/server/src/main/resources/test.toml:
--------------------------------------------------------------------------------
1 | [user]
2 | name="huang"
3 | age=25
4 | sex=1
5 | [user]
6 | name="zhang"
7 | age=18
8 | sex=2
9 |
10 |
--------------------------------------------------------------------------------
/common/src/main/java/com/fys/cmd/exception/AuthenticationException.java:
--------------------------------------------------------------------------------
1 | package com.fys.cmd.exception;
2 |
3 | /**
4 | * hcy 2020/2/24
5 | */
6 | public class AuthenticationException extends RuntimeException {
7 |
8 | public static AuthenticationException INSTANCE = new AuthenticationException();
9 |
10 | public AuthenticationException() {
11 | super("密码不对");
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/common/src/main/java/com/fys/cmd/message/Ping.java:
--------------------------------------------------------------------------------
1 | package com.fys.cmd.message;
2 |
3 | import io.netty.buffer.ByteBuf;
4 |
5 | /**
6 | * hcy 2020/2/18
7 | */
8 | public class Ping implements Cmd {
9 |
10 | private static Ping instance = new Ping();
11 |
12 | @Override
13 | public void encoderTo(ByteBuf buf) {
14 | buf.writeByte(Cmd.ping);
15 | }
16 |
17 | public static Ping decoderFrom(ByteBuf in) {
18 | return instance;
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/common/src/main/java/com/fys/cmd/message/Pong.java:
--------------------------------------------------------------------------------
1 | package com.fys.cmd.message;
2 |
3 | import io.netty.buffer.ByteBuf;
4 |
5 | /**
6 | * hcy 2020/2/18
7 | */
8 | public class Pong implements Cmd {
9 |
10 | private static Pong instance = new Pong();
11 |
12 | @Override
13 | public void encoderTo(ByteBuf buf) {
14 | buf.writeByte(Cmd.pong);
15 | }
16 |
17 | public static Pong decoderFrom(ByteBuf in) {
18 | return instance;
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/server/src/main/resources/logback-test.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | ${consolePattern}
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/client/src/main/resources/logback-test.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | ${consolePattern}
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/common/src/main/java/com/fys/cmd/handler/CmdEncoder.java:
--------------------------------------------------------------------------------
1 | package com.fys.cmd.handler;
2 |
3 | import com.fys.cmd.message.Cmd;
4 | import io.netty.buffer.ByteBuf;
5 | import io.netty.channel.ChannelHandlerContext;
6 | import io.netty.handler.codec.MessageToByteEncoder;
7 |
8 | /**
9 | * hcy 2020/2/18
10 | */
11 | public class CmdEncoder extends MessageToByteEncoder {
12 |
13 | //输出
14 | @Override
15 | protected void encode(ChannelHandlerContext ctx, Cmd msg, ByteBuf out) {
16 | msg.encoderTo(out);
17 | }
18 |
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/server/src/main/resources/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "server": {
3 | "bindHost": 9050,
4 | "token": "123456"
5 | },
6 | "clients": [
7 | {
8 | "clientName": "hcy_home_pc",
9 | "serverWorkers": [
10 | {
11 | "serverPort": "9051",
12 | "localHost": "0.0.0.0",
13 | "localPort": "81"
14 | },
15 | {
16 | "serverPort": "9052",
17 | "localHost": "0.0.0.0",
18 | "localPort": "81"
19 | },
20 | {
21 | "serverPort": "9053",
22 | "localHost": "0.0.0.0",
23 | "localPort": "82"
24 | }
25 | ]
26 | }
27 | ]
28 | }
--------------------------------------------------------------------------------
/common/src/main/java/com/fys/cmd/message/Cmd.java:
--------------------------------------------------------------------------------
1 | package com.fys.cmd.message;
2 |
3 | import io.netty.buffer.ByteBuf;
4 |
5 | /**
6 | * hcy 2020/2/10
7 | * 消息总是由 长度 + data 的形式组成的,不同消息的data形式不同,看具体实现
8 | */
9 | public interface Cmd {
10 |
11 | byte dataConnectionCmd = 0;
12 | byte ping = 4;
13 | byte pong = 6;
14 |
15 |
16 | interface ServerToClient {
17 | byte serverStartSuccessCmd = 2;
18 | byte serverStartFailCmd = 3;
19 | byte loginFail = 7;
20 | }
21 |
22 | interface ClientToServer {
23 | byte login = 5;
24 | }
25 |
26 |
27 | /*
28 | * 将当前对象序列化到Bytebuf中
29 | * */
30 | void encoderTo(ByteBuf buf);
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/server/src/main/java/com/fys/conf/ServerInfo.java:
--------------------------------------------------------------------------------
1 | package com.fys.conf;
2 |
3 | import java.util.List;
4 |
5 | /**
6 | * hcy 2020/3/26
7 | */
8 | public class ServerInfo {
9 |
10 | private String clientName;
11 | private List serverWorkers;
12 |
13 | public String getClientName() {
14 | return clientName;
15 | }
16 |
17 | public void setClientName(String clientName) {
18 | this.clientName = clientName;
19 | }
20 |
21 | public List getServerWorkers() {
22 | return serverWorkers;
23 | }
24 |
25 | public void setServerWorkers(List serverWorkers) {
26 | this.serverWorkers = serverWorkers;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/client/src/main/java/com/fys/handler/LoginFailHandler.java:
--------------------------------------------------------------------------------
1 | package com.fys.handler;
2 |
3 | import com.fys.cmd.message.serverToClient.LoginFailCmd;
4 | import io.netty.channel.ChannelHandlerContext;
5 | import io.netty.channel.SimpleChannelInboundHandler;
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 |
9 | /**
10 | * hcy 2020/3/26
11 | */
12 | public class LoginFailHandler extends SimpleChannelInboundHandler {
13 | private static Logger log = LoggerFactory.getLogger(LoginFailCmd.class);
14 |
15 | @Override
16 | protected void channelRead0(ChannelHandlerContext ctx, LoginFailCmd msg) throws Exception {
17 | log.info(msg.toString());
18 | ctx.close();
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/client/src/main/java/com/fys/handler/LoginHandler.java:
--------------------------------------------------------------------------------
1 | package com.fys.handler;
2 |
3 | import com.fys.AppClient;
4 | import com.fys.Config;
5 | import com.fys.cmd.message.clientToServer.LoginCmd;
6 | import io.netty.channel.ChannelHandlerContext;
7 | import io.netty.channel.ChannelInboundHandlerAdapter;
8 |
9 | /**
10 | * hcy 2020/5/8
11 | * 连接成功后则发送登陆信息
12 | */
13 | public class LoginHandler extends ChannelInboundHandlerAdapter {
14 |
15 | @Override
16 | public void channelActive(ChannelHandlerContext ctx) throws Exception {
17 | Config config = ctx.channel().attr(AppClient.key).get();
18 | ctx.writeAndFlush(new LoginCmd(config.getClientName()));
19 | super.channelActive(ctx);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/common/src/main/java/com/fys/cmd/listener/ErrorLogListener.java:
--------------------------------------------------------------------------------
1 | package com.fys.cmd.listener;
2 |
3 | import io.netty.channel.ChannelFuture;
4 | import io.netty.channel.ChannelFutureListener;
5 | import org.slf4j.Logger;
6 | import org.slf4j.LoggerFactory;
7 |
8 | /**
9 | * hcy 2020/2/22
10 | */
11 | public class ErrorLogListener implements ChannelFutureListener {
12 |
13 | private static Logger log = LoggerFactory.getLogger(ErrorLogListener.class);
14 |
15 | public static ErrorLogListener INSTANCE = new ErrorLogListener();
16 |
17 | @Override
18 | public void operationComplete(ChannelFuture future) {
19 | if (!future.isSuccess()) {
20 | log.error("", future.cause());
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/common/src/test/java/com/fys/cmd/message/clientToServer/LoginCmdTest.java:
--------------------------------------------------------------------------------
1 | package com.fys.cmd.message.clientToServer;
2 |
3 | import com.fys.cmd.message.Cmd;
4 | import io.netty.buffer.ByteBuf;
5 | import io.netty.buffer.Unpooled;
6 | import org.junit.Assert;
7 | import org.junit.Test;
8 |
9 | /**
10 | * hcy 2020/3/25
11 | */
12 | public class LoginCmdTest {
13 |
14 | @Test
15 | public void test() {
16 | LoginCmd login = new LoginCmd("啦啦啦123abc");
17 | ByteBuf buffer = Unpooled.buffer();
18 | login.encoderTo(buffer);
19 |
20 | Assert.assertEquals(Cmd.ClientToServer.login, buffer.readByte());
21 | LoginCmd decode = LoginCmd.decoderFrom(buffer);
22 | Assert.assertEquals(login,decode);
23 | }
24 |
25 | }
--------------------------------------------------------------------------------
/common/src/test/java/com/fys/cmd/message/serverToClient/LoginFailCmdTest.java:
--------------------------------------------------------------------------------
1 | package com.fys.cmd.message.serverToClient;
2 |
3 | import com.fys.cmd.message.Cmd;
4 | import io.netty.buffer.ByteBuf;
5 | import io.netty.buffer.Unpooled;
6 | import org.junit.Test;
7 |
8 | import static org.junit.Assert.assertEquals;
9 |
10 | /**
11 | * hcy 2020/3/26
12 | */
13 | public class LoginFailCmdTest {
14 |
15 | @Test
16 | public void encoderTo() {
17 | LoginFailCmd msg = new LoginFailCmd("clientname拉拉", "error msg啦啦啦");
18 | ByteBuf buffer = Unpooled.buffer();
19 | msg.encoderTo(buffer);
20 | assertEquals(Cmd.ServerToClient.loginFail, buffer.readByte());
21 | assertEquals(msg, LoginFailCmd.decoderFrom(buffer));
22 |
23 | }
24 | }
--------------------------------------------------------------------------------
/client/src/main/java/com/fys/handler/ServerStartSuccessHandler.java:
--------------------------------------------------------------------------------
1 | package com.fys.handler;
2 |
3 | import com.fys.cmd.message.serverToClient.ServerStartSuccessCmd;
4 | import io.netty.channel.ChannelHandler;
5 | import io.netty.channel.ChannelHandlerContext;
6 | import io.netty.channel.SimpleChannelInboundHandler;
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 |
10 | /**
11 | * hcy 2020/2/19
12 | * 此类处理服务器创建成功事件
13 | */
14 | @ChannelHandler.Sharable
15 | public class ServerStartSuccessHandler extends SimpleChannelInboundHandler {
16 |
17 | private Logger log = LoggerFactory.getLogger(ServerStartSuccessHandler.class);
18 |
19 | @Override
20 | protected void channelRead0(ChannelHandlerContext ctx, ServerStartSuccessCmd msg) {
21 | log.info(msg.toString());
22 | }
23 |
24 | }
--------------------------------------------------------------------------------
/common/src/test/java/com/fys/cmd/message/serverToClient/ServerStartSuccessCmdTest.java:
--------------------------------------------------------------------------------
1 | package com.fys.cmd.message.serverToClient;
2 |
3 | import com.fys.cmd.message.Cmd;
4 | import io.netty.buffer.ByteBuf;
5 | import io.netty.buffer.Unpooled;
6 | import org.junit.Test;
7 |
8 | import static org.junit.Assert.assertEquals;
9 |
10 | /**
11 | * hcy 2020/2/22
12 | */
13 | public class ServerStartSuccessCmdTest {
14 |
15 | @Test
16 | public void encoderTo() {
17 | ByteBuf buffer = Unpooled.buffer();
18 | ServerStartSuccessCmd src = new ServerStartSuccessCmd(70, "127.0.5.7", 50);
19 | src.encoderTo(buffer);
20 | assertEquals(Cmd.ServerToClient.serverStartSuccessCmd, buffer.readByte());
21 | ServerStartSuccessCmd dec = ServerStartSuccessCmd.decoderFrom(buffer);
22 | assertEquals(src, dec);
23 | buffer.release();
24 | }
25 | }
--------------------------------------------------------------------------------
/client/src/main/java/com/fys/handler/ServerStartFailHandler.java:
--------------------------------------------------------------------------------
1 | package com.fys.handler;
2 |
3 | import com.fys.cmd.message.serverToClient.ServerStartFailCmd;
4 | import io.netty.channel.ChannelHandler;
5 | import io.netty.channel.ChannelHandlerContext;
6 | import io.netty.channel.SimpleChannelInboundHandler;
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 |
10 | /**
11 | * hcy 2020/2/19
12 | */
13 | @ChannelHandler.Sharable
14 | public class ServerStartFailHandler extends SimpleChannelInboundHandler {
15 |
16 | private Logger log = LoggerFactory.getLogger(ServerStartFailHandler.class);
17 |
18 | @Override
19 | protected void channelRead0(ChannelHandlerContext ctx, ServerStartFailCmd msg) {
20 | log.error(msg.toString());
21 | if (ctx.channel().isActive()) {
22 | ctx.close();
23 | }
24 | }
25 |
26 | }
--------------------------------------------------------------------------------
/common/src/test/java/com/fys/cmd/message/serverToClient/ServerStartFailCmdTest.java:
--------------------------------------------------------------------------------
1 | package com.fys.cmd.message.serverToClient;
2 |
3 | import com.fys.cmd.message.Cmd;
4 | import io.netty.buffer.ByteBuf;
5 | import io.netty.buffer.Unpooled;
6 | import org.junit.Test;
7 |
8 | import java.util.UUID;
9 |
10 | import static org.junit.Assert.assertEquals;
11 |
12 | /**
13 | * hcy 2020/2/22
14 | */
15 | public class ServerStartFailCmdTest {
16 |
17 | @Test
18 | public void encoderTo() {
19 | ByteBuf buffer = Unpooled.buffer();
20 | ServerStartFailCmd src = new ServerStartFailCmd(80,"如果是中文会报错吗", 70, UUID.randomUUID().toString());
21 | src.encoderTo(buffer);
22 | assertEquals(Cmd.ServerToClient.serverStartFailCmd, buffer.readByte());
23 | ServerStartFailCmd dec = ServerStartFailCmd.decoderFrom(buffer);
24 | assertEquals(src, dec);
25 | buffer.release();
26 | }
27 | }
--------------------------------------------------------------------------------
/common/src/main/java/com/fys/cmd/util/CodeUtil.java:
--------------------------------------------------------------------------------
1 | package com.fys.cmd.util;
2 |
3 | import java.security.MessageDigest;
4 | import java.security.NoSuchAlgorithmException;
5 |
6 | /**
7 | * hcy 2020/4/3
8 | */
9 | public class CodeUtil {
10 |
11 | public static byte[] md5(byte[] src) {
12 | try {
13 | MessageDigest md = MessageDigest.getInstance("md5");
14 | return md.digest(src);
15 | } catch (NoSuchAlgorithmException e) {
16 | throw new RuntimeException(e);
17 | }
18 | }
19 |
20 | public static byte[] md5(byte[] src, byte[] src2) {
21 | try {
22 | MessageDigest md = MessageDigest.getInstance("md5");
23 | md.update(src);
24 | md.update(src2);
25 | return md.digest();
26 | } catch (NoSuchAlgorithmException e) {
27 | throw new RuntimeException(e);
28 | }
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/common/src/main/java/com/fys/cmd/handler/TimeOutHandler.java:
--------------------------------------------------------------------------------
1 | package com.fys.cmd.handler;
2 |
3 | import io.netty.channel.ChannelHandlerContext;
4 | import io.netty.handler.timeout.IdleStateEvent;
5 | import io.netty.handler.timeout.IdleStateHandler;
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 |
9 | /**
10 | * hcy 2020/2/21
11 | */
12 | public class TimeOutHandler extends IdleStateHandler {
13 |
14 | private static Logger log = LoggerFactory.getLogger(TimeOutHandler.class);
15 |
16 | public TimeOutHandler(int readerIdleTimeSeconds, int writerIdleTimeSeconds, int allIdleTimeSeconds) {
17 | super(readerIdleTimeSeconds, writerIdleTimeSeconds, allIdleTimeSeconds);
18 | }
19 |
20 |
21 | @Override
22 | public final void channelIdle(ChannelHandlerContext ctx, IdleStateEvent evt) throws Exception {
23 | log.debug("超时关闭连接:{},Event:{}", ctx, evt);
24 | ctx.flush().close();
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/common/src/test/java/com/fys/cmd/message/DataConnectionCmdTest.java:
--------------------------------------------------------------------------------
1 | package com.fys.cmd.message;
2 |
3 | import io.netty.buffer.ByteBuf;
4 | import io.netty.buffer.Unpooled;
5 | import org.junit.Test;
6 |
7 | import static org.junit.Assert.assertEquals;
8 |
9 | /**
10 | * hcy 2020/2/23
11 | */
12 | public class DataConnectionCmdTest {
13 |
14 | @Test
15 | public void encoderTo() {
16 | ByteBuf buffer = Unpooled.buffer();
17 | DataConnectionCmd src = new DataConnectionCmd( 90, "127.0.2.8", 80, System.nanoTime());
18 | src.encoderTo(buffer);
19 | assertEquals(Cmd.dataConnectionCmd, buffer.readByte());
20 | DataConnectionCmd dec = DataConnectionCmd.decoderFrom(buffer);
21 | assertEquals(src.getLocalHost(), dec.getLocalHost());
22 | assertEquals(src.getLocalPort(), dec.getLocalPort());
23 | assertEquals(src.getServerPort(), dec.getServerPort());
24 | assertEquals(src.getToken(), dec.getToken());
25 | buffer.release();
26 | }
27 | }
--------------------------------------------------------------------------------
/server/src/test/java/com/fys/H2Test.java:
--------------------------------------------------------------------------------
1 | package com.fys;
2 |
3 | import org.h2.jdbcx.JdbcConnectionPool;
4 | import org.h2.tools.Server;
5 |
6 | import java.sql.Connection;
7 | import java.sql.DriverManager;
8 | import java.sql.SQLException;
9 |
10 | /**
11 | * hcy 2020/2/26
12 | */
13 | public class H2Test {
14 |
15 | public static void main(String[] args) throws SQLException, ClassNotFoundException {
16 |
17 |
18 | JdbcConnectionPool cp = JdbcConnectionPool.create("jdbc:h2:~/test", "", "");
19 | Connection conn = cp.getConnection();
20 | conn.close();
21 | cp.dispose();
22 |
23 | // Class.forName("org.h2.Driver");
24 | // Connection conn = DriverManager.getConnection("jdbc:h2:~/test", "sa", "");
25 | //
26 | // Server.createWebServer().start();
27 |
28 | //默认8082 端口
29 |
30 | Class.forName("org.h2.Driver");
31 | Connection conn1 = DriverManager.getConnection("jdbc:h2:tcp:localhost:9092/~/test", "sa", "");
32 |
33 | }
34 |
35 |
36 | }
--------------------------------------------------------------------------------
/server/src/test/java/com/fys/ConfigTest.java:
--------------------------------------------------------------------------------
1 | package com.fys;
2 |
3 | import com.fys.conf.ServerInfo;
4 | import com.fys.conf.ServerWorker;
5 | import org.junit.Assert;
6 | import org.junit.Test;
7 |
8 | import java.io.IOException;
9 |
10 | /**
11 | * hcy 2020/3/26
12 | */
13 | public class ConfigTest {
14 |
15 | @Test
16 | public void testConf() throws IOException {
17 | Config.init("config.json");
18 | Assert.assertEquals("0.0.0.0", Config.bindHost);
19 | Assert.assertEquals(9050, Config.bindPort);
20 | ServerInfo hcy_home_pc = Config.getServerInfo("hcy_home_pc");
21 | System.out.println(hcy_home_pc.getClientName());
22 | for (ServerWorker serverWorker : hcy_home_pc.getServerWorkers()) {
23 | System.out.println("------");
24 | System.out.println(serverWorker.getServerPort());
25 | System.out.println(serverWorker.getLocalHost());
26 | System.out.println(serverWorker.getLocalPort());
27 | }
28 |
29 | }
30 |
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/common/src/test/java/com/fys/cmd/handler/Rc4Md5HandlerTest.java:
--------------------------------------------------------------------------------
1 | package com.fys.cmd.handler;
2 |
3 | import io.netty.buffer.ByteBuf;
4 | import io.netty.buffer.ByteBufUtil;
5 | import io.netty.buffer.Unpooled;
6 | import io.netty.channel.embedded.EmbeddedChannel;
7 | import org.junit.Assert;
8 | import org.junit.Test;
9 |
10 | /**
11 | * hcy 2020/4/3
12 | */
13 | public class Rc4Md5HandlerTest {
14 |
15 | @Test
16 | public void decode() {
17 |
18 | EmbeddedChannel eb = new EmbeddedChannel(new Rc4Md5Handler("123456"));
19 | //出站将数据加密
20 | ByteBuf origin = Unpooled.wrappedBuffer("abcdefghijklmn".getBytes());
21 | //将副本写出channel
22 | ByteBuf src = origin.copy();
23 | Assert.assertTrue(eb.writeOutbound(src));
24 | //出站rc4加密后的数据
25 | Object o = eb.readOutbound();
26 |
27 | //进站,解密数据
28 | Assert.assertTrue(eb.writeInbound(o));
29 | //读取解密后的数据
30 | ByteBuf buff = eb.readInbound();
31 | Assert.assertTrue(ByteBufUtil.equals(origin, buff));
32 | Assert.assertFalse(eb.finish());
33 | eb.close();
34 | }
35 | }
--------------------------------------------------------------------------------
/common/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 | com.remotePort
7 | remotePortMapping
8 | 1.0-SNAPSHOT
9 |
10 |
11 | 4.0.0
12 |
13 | common
14 | common
15 |
16 |
17 |
18 | io.netty
19 | netty-all
20 |
21 |
22 | ch.qos.logback
23 | logback-classic
24 |
25 |
26 | junit
27 | junit
28 | test
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/client/src/main/java/com/fys/handler/DataConnectionHandler.java:
--------------------------------------------------------------------------------
1 | package com.fys.handler;
2 |
3 | import com.fys.AppClient;
4 | import com.fys.Config;
5 | import com.fys.DataConnectionClient;
6 | import com.fys.cmd.message.DataConnectionCmd;
7 | import io.netty.channel.ChannelHandlerContext;
8 | import io.netty.channel.SimpleChannelInboundHandler;
9 | import io.netty.util.Attribute;
10 | import org.slf4j.Logger;
11 | import org.slf4j.LoggerFactory;
12 |
13 |
14 | public class DataConnectionHandler extends SimpleChannelInboundHandler {
15 |
16 | private Logger log = LoggerFactory.getLogger(DataConnectionHandler.class);
17 |
18 | /*
19 | * 需要注意这个ctx是managerChannel的,即使开启失败也不能关闭此ctx
20 | * */
21 | @Override
22 | protected void channelRead0(ChannelHandlerContext ctx, DataConnectionCmd msg) {
23 | Attribute conf = ctx.channel().attr(AppClient.key);
24 | Config config = conf.get();
25 | log.debug("收到服务端DataConnection,{} -> {}:{}", msg.getServerPort(), msg.getLocalHost(), msg.getLocalPort());
26 | new DataConnectionClient(config, msg).start();
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/common/src/main/java/com/fys/cmd/handler/ExceptionHandler.java:
--------------------------------------------------------------------------------
1 | package com.fys.cmd.handler;
2 |
3 | import io.netty.channel.Channel;
4 | import io.netty.channel.ChannelHandler;
5 | import io.netty.channel.ChannelHandlerContext;
6 | import io.netty.channel.ChannelInboundHandlerAdapter;
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 |
10 | /**
11 | * hcy 2020/2/22
12 | */
13 | @ChannelHandler.Sharable
14 | public class ExceptionHandler extends ChannelInboundHandlerAdapter {
15 |
16 | private static Logger log = LoggerFactory.getLogger(ExceptionHandler.class);
17 | public static ExceptionHandler INSTANCE = new ExceptionHandler();
18 |
19 | public static final String NAME = "ExceptionHandler";
20 |
21 | private ExceptionHandler() {
22 | }
23 |
24 | @Override
25 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
26 | Channel channel = ctx.channel();
27 | if ("Connection reset by peer".equals(cause.getMessage())) {
28 | log.error("收尾:Connection reset by peer local:{},remote:{}", channel.localAddress(), channel.remoteAddress());
29 | return;
30 | }
31 | log.error("收尾local:" + channel.localAddress() + " remote" + channel.remoteAddress(), cause);
32 | ctx.close();
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/server/src/main/java/com/fys/conf/ServerWorker.java:
--------------------------------------------------------------------------------
1 | package com.fys.conf;
2 |
3 | /**
4 | * hcy 2020/3/2
5 | */
6 | public class ServerWorker {
7 |
8 | private Integer serverPort;
9 | private String localHost;
10 | private Integer localPort;
11 |
12 | public ServerWorker() {
13 | }
14 |
15 | public ServerWorker(Integer serverPort, String localHost, Integer localPort) {
16 | this.serverPort = serverPort;
17 | this.localHost = localHost;
18 | this.localPort = localPort;
19 | }
20 |
21 | public Integer getServerPort() {
22 | return serverPort;
23 | }
24 |
25 | public void setServerPort(Integer serverPort) {
26 | this.serverPort = serverPort;
27 | }
28 |
29 | public String getLocalHost() {
30 | return localHost;
31 | }
32 |
33 | public void setLocalHost(String localHost) {
34 | this.localHost = localHost;
35 | }
36 |
37 | public Integer getLocalPort() {
38 | return localPort;
39 | }
40 |
41 | public void setLocalPort(Integer localPort) {
42 | this.localPort = localPort;
43 | }
44 |
45 |
46 | @Override
47 | public String toString() {
48 | if (serverPort == null) {
49 | return "尚未配置的服务映射";
50 | }
51 | return serverPort + " ---> " + localHost + ":" + localPort;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/client/src/main/java/com/fys/Config.java:
--------------------------------------------------------------------------------
1 | package com.fys;
2 |
3 | import java.io.FileInputStream;
4 | import java.io.InputStream;
5 | import java.nio.file.Files;
6 | import java.nio.file.Paths;
7 | import java.util.Properties;
8 |
9 | /**
10 | * hcy 2020/2/18
11 | */
12 | public class Config {
13 |
14 | private String serverIp;
15 | private int serverPort;
16 | private String clientName;
17 | private String token;
18 |
19 | public static Config INSTANCE = new Config();
20 |
21 | private Config() {
22 | try {
23 | InputStream in = ClassLoader.getSystemResourceAsStream("config.properties");
24 | if (in == null) {
25 | if (Files.notExists(Paths.get("config.properties"))) {
26 | throw new RuntimeException("找不到配置文件: config.properties");
27 | }
28 | in = new FileInputStream("config.properties");
29 | }
30 |
31 | Properties prop = new Properties();
32 | prop.load(in);
33 | serverIp = prop.getProperty("serverIp");
34 | clientName = prop.getProperty("clientName");
35 | token = prop.getProperty("token");
36 | serverPort = Integer.parseInt(prop.getProperty("serverPort"));
37 | in.close();
38 | } catch (Exception e) {
39 | e.printStackTrace();
40 | }
41 | }
42 |
43 | public String getServerIp() {
44 | return serverIp;
45 | }
46 |
47 | public int getServerPort() {
48 | return serverPort;
49 | }
50 |
51 | public String getClientName() {
52 | return clientName;
53 | }
54 |
55 | public String getToken() {
56 | return token;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/client/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | ${consolePattern}
11 |
12 |
13 |
14 |
15 | ${filePattern}
16 |
17 | client_info_log.log
18 |
19 | client_info_log_%d{yyyy-MM-dd}.%i.gz
20 | 10MB
21 | 5
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | ${filePattern}
33 |
34 | client_debug_log.log
35 |
36 | client_debug_log_%d{yyyy-MM-dd}.%i.gz
37 | 10MB
38 | 5
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ### 这是一个java实现的内网穿透工具。
2 | 它能实现通过服务器做跳板来穿透到内网机器上。
3 | 他能实现客户端服务端数据传输加密
4 | 他能实现多客户端同时使用
5 | 他能实现同时映射多个端口
6 |
7 | ### 客户端用法
8 |
9 | 客户端默认会查找jar文件相同目录下的'config.properties'的配置文件,
10 | 配置文件在resources目录下,按照这个模板配置。
11 |
12 | ```properties
13 | #服务器ip
14 | serverIp=127.0.0.1
15 | #服务器端口
16 | serverPort=9050
17 | #客户端名
18 | clientName=hcy_home_pc
19 | #密码
20 | token=123456
21 | ```
22 |
23 |
24 |
25 |
26 |
27 | ### 服务端用法配置
28 |
29 | 服务端需要绑定一个管理端口用于客户端管理,再绑定一个端口用于映射。
30 | 请在jar文件相同目录下创建一个 config.json的文件,配置如下。
31 |
32 | ```json
33 | {
34 | "server": {
35 | "bindHost": 9050,
36 | "token": "123456"
37 | },
38 | "clients": [
39 | {
40 | "clientName": "hcy_home_pc",
41 | "serverWorkers": [
42 | {
43 | "serverPort": "9051",
44 | "localHost": "0.0.0.0",
45 | "localPort": "81"
46 | },
47 | {
48 | "serverPort": "9052",
49 | "localHost": "0.0.0.0",
50 | "localPort": "81"
51 | },
52 | {
53 | "serverPort": "9053",
54 | "localHost": "0.0.0.0",
55 | "localPort": "82"
56 | }
57 | ]
58 | }
59 | ]
60 | }
61 | ```
62 |
63 |
64 | - 服务器只允许具有相同Token的客户端连接
65 | - 服务器只能为配置在clients节点下的client服务,如果Token正确client下找不到对应名称的client,也是不能工作的
66 | - 所有配置只在服务器上配置,客户端只能决定连接还是断开
67 |
68 |
69 |
70 | ##### 只有配置了映射关系服务器才会监听该端口,并且要保证服务器的指定端口是没有被使用的。
71 |
72 |
73 |
74 | ### 配置window远程桌面示例
75 | 即在家里远程桌面连接, 就可以连接到公司电脑了。
76 | ```json
77 |
78 | {
79 | "server": {
80 | "bindHost": 9050,
81 | "token": "123456"
82 | },
83 | "clients": [
84 | {
85 | "clientName": "hcy_home_pc",
86 | "serverWorkers": [
87 | {
88 | "serverPort": "9051",
89 | "localHost": "0.0.0.0",
90 | "localPort": 3389
91 | }
92 | ]
93 | }
94 | ]
95 | }
96 | ```
97 |
98 |
99 | ## 一定要注意,因为用到了随机数,启动java时要使用伪随机数,不然linux的随机数是堵塞的
--------------------------------------------------------------------------------
/common/src/main/java/com/fys/cmd/message/serverToClient/LoginFailCmd.java:
--------------------------------------------------------------------------------
1 | package com.fys.cmd.message.serverToClient;
2 |
3 | import com.fys.cmd.message.Cmd;
4 | import io.netty.buffer.ByteBuf;
5 | import io.netty.buffer.ByteBufUtil;
6 |
7 | import java.util.Objects;
8 |
9 | import static java.nio.charset.StandardCharsets.UTF_8;
10 |
11 | /**
12 | * hcy 2020/3/26
13 | */
14 | public class LoginFailCmd implements Cmd {
15 | private String clientName;
16 | private String failMsg;
17 |
18 | public LoginFailCmd(String clientName, String failMsg) {
19 | this.clientName = clientName;
20 | this.failMsg = failMsg;
21 | }
22 |
23 | @Override
24 | public void encoderTo(ByteBuf buf) {
25 | buf.writeByte(ServerToClient.loginFail);
26 | buf.writeInt(ByteBufUtil.utf8Bytes(clientName));
27 | buf.writeCharSequence(clientName, UTF_8);
28 | buf.writeInt(ByteBufUtil.utf8Bytes(failMsg));
29 | buf.writeCharSequence(failMsg, UTF_8);
30 | }
31 |
32 | public static LoginFailCmd decoderFrom(ByteBuf in) {
33 | CharSequence clientName = in.readCharSequence(in.readInt(), UTF_8);
34 | CharSequence failMsg = in.readCharSequence(in.readInt(), UTF_8);
35 | return new LoginFailCmd(clientName.toString(), failMsg.toString());
36 | }
37 |
38 | @Override
39 | public boolean equals(Object o) {
40 | if (this == o) return true;
41 | if (o == null || getClass() != o.getClass()) return false;
42 | LoginFailCmd that = (LoginFailCmd) o;
43 | return Objects.equals(clientName, that.clientName) &&
44 | Objects.equals(failMsg, that.failMsg);
45 | }
46 |
47 | @Override
48 | public int hashCode() {
49 | return Objects.hash(clientName, failMsg);
50 | }
51 |
52 | @Override
53 | public String toString() {
54 | return "登录失败:[clientName:+" + clientName + ",+FailMsg:" + failMsg + "]";
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/common/src/main/java/com/fys/cmd/message/clientToServer/LoginCmd.java:
--------------------------------------------------------------------------------
1 | package com.fys.cmd.message.clientToServer;
2 |
3 | import com.fys.cmd.exception.AuthenticationException;
4 | import com.fys.cmd.message.Cmd;
5 | import io.netty.buffer.ByteBuf;
6 | import io.netty.buffer.ByteBufUtil;
7 |
8 | import java.util.Objects;
9 |
10 | import static java.nio.charset.StandardCharsets.UTF_8;
11 |
12 | /**
13 | * hcy 2020/3/25
14 | * 登录
15 | */
16 | public class LoginCmd implements Cmd {
17 |
18 | private String clientName;
19 |
20 | public LoginCmd(String clientName) {
21 | this.clientName = clientName;
22 | }
23 |
24 | // flag 长度 clientName md5
25 | @Override
26 | public void encoderTo(ByteBuf buf) {
27 | buf.writeByte(ClientToServer.login);
28 | buf.writeInt(ByteBufUtil.utf8Bytes(clientName));
29 | buf.writeCharSequence(clientName, UTF_8);
30 | }
31 |
32 | public static LoginCmd decoderFrom(ByteBuf in) {
33 | int clientNameLength = in.readInt();
34 | if (clientNameLength > 50) {
35 | throw new AuthenticationException();
36 | }
37 | CharSequence clientName = in.readCharSequence(clientNameLength, UTF_8);
38 | return new LoginCmd(clientName.toString());
39 | }
40 |
41 | public String getClientName() {
42 | return clientName;
43 | }
44 |
45 | @Override
46 | public boolean equals(Object o) {
47 | if (this == o) return true;
48 | if (o == null || getClass() != o.getClass()) return false;
49 | LoginCmd loginCmd = (LoginCmd) o;
50 | return Objects.equals(clientName, loginCmd.clientName);
51 | }
52 |
53 | @Override
54 | public int hashCode() {
55 |
56 | return Objects.hash(clientName);
57 | }
58 |
59 | @Override
60 | public String toString() {
61 | return "LoginCmd{" +
62 | "clientName='" + clientName + '\'' +
63 | '}';
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/client/src/main/java/com/fys/handler/CmdDecoder.java:
--------------------------------------------------------------------------------
1 | package com.fys.handler;
2 |
3 | import com.fys.cmd.message.Cmd;
4 | import com.fys.cmd.message.DataConnectionCmd;
5 | import com.fys.cmd.message.Pong;
6 | import com.fys.cmd.message.serverToClient.LoginFailCmd;
7 | import com.fys.cmd.message.Ping;
8 | import com.fys.cmd.message.serverToClient.ServerStartFailCmd;
9 | import com.fys.cmd.message.serverToClient.ServerStartSuccessCmd;
10 | import io.netty.buffer.ByteBuf;
11 | import io.netty.channel.ChannelHandlerContext;
12 | import io.netty.handler.codec.ReplayingDecoder;
13 | import org.slf4j.Logger;
14 | import org.slf4j.LoggerFactory;
15 |
16 | import java.util.List;
17 |
18 | /**
19 | * hcy 2020/2/18
20 | */
21 | public class CmdDecoder extends ReplayingDecoder {
22 |
23 | private static Logger log = LoggerFactory.getLogger(CmdDecoder.class);
24 |
25 | @Override
26 | protected void decode(ChannelHandlerContext ctx, ByteBuf in, List