diff --git a/neutrino-core/src/main/java/fun/asgc/neutrino/core/db/template/JdbcOperations.java b/neutrino-core/src/main/java/fun/asgc/neutrino/core/db/template/JdbcOperations.java index 483da757..4fe86a20 100644 --- a/neutrino-core/src/main/java/fun/asgc/neutrino/core/db/template/JdbcOperations.java +++ b/neutrino-core/src/main/java/fun/asgc/neutrino/core/db/template/JdbcOperations.java @@ -21,15 +21,15 @@ */ package fun.asgc.neutrino.core.db.template; +import fun.asgc.neutrino.core.db.annotation.Id; +import fun.asgc.neutrino.core.util.CollectionUtil; import fun.asgc.neutrino.core.util.ReflectUtil; import fun.asgc.neutrino.core.util.TypeUtil; import java.lang.reflect.Field; import java.sql.*; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; /** * @@ -38,6 +38,8 @@ import java.util.Map; */ public class JdbcOperations { private static final JdbcOperations instance = new JdbcOperations(); + + private static final Map, Field> generateIdFieldMap = new ConcurrentHashMap<>(); private JdbcOperations() { @@ -85,6 +87,45 @@ public class JdbcOperations { }); } + /** + * 执行更新操作 + * 临时兼容返设主键问题 + * @param conn + * @param sql + * @param params + * @return + */ + public int executeUpdateByModel(final Connection conn , final String sql, final Object model, final Object[] params) throws SQLException { + return this.execute(new PreparedStatementJdbcCallback(){ + @Override + public Integer execute(PreparedStatement ps) throws SQLException { + Integer res = ps.executeUpdate(); + if (null != model) { + ResultSet resultSet = ps.getGeneratedKeys(); + if (resultSet.next()) { + Field field = getGenerateIdField(model.getClass()); + if (null != field) { + ReflectUtil.setFieldValue(field, model, resultSet.getInt(1)); + } + } + } + return res; + } + @Override + public Object[] getParams() { + return params; + } + @Override + public String getSql() { + return sql; + } + @Override + public Connection getConnection(){ + return conn; + } + }); + } + /** * 执行单条查询操作 * @param conn @@ -225,4 +266,28 @@ public class JdbcOperations { }); } + + /** + * 获取自动生成ID字段 + * @param clazz + * @return + */ + private static Field getGenerateIdField(Class clazz) { + if (null == clazz) { + return null; + } + if (generateIdFieldMap.containsKey(clazz)) { + return generateIdFieldMap.get(clazz); + } + Set fields = ReflectUtil.getDeclaredFields(clazz); + if (CollectionUtil.isEmpty(fields)) { + return null; + } + Field field = fields.stream().filter(f -> f.isAnnotationPresent(Id.class)).findFirst().orElse(null); + if (null != field) { + return field; + } + field = fields.stream().filter(f -> f.getName().equals("id")).findFirst().orElse(null); + return field; + } } diff --git a/neutrino-core/src/main/java/fun/asgc/neutrino/core/db/template/JdbcTemplate.java b/neutrino-core/src/main/java/fun/asgc/neutrino/core/db/template/JdbcTemplate.java index beddcf43..5bab3bf9 100644 --- a/neutrino-core/src/main/java/fun/asgc/neutrino/core/db/template/JdbcTemplate.java +++ b/neutrino-core/src/main/java/fun/asgc/neutrino/core/db/template/JdbcTemplate.java @@ -21,16 +21,12 @@ */ package fun.asgc.neutrino.core.db.template; -import fun.asgc.neutrino.core.util.ArrayUtil; - import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException; -import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; /** * @@ -74,6 +70,31 @@ public class JdbcTemplate { return res; } + /** + * TODO 临时用来兼容insert之后需要返设主键的问题 + * @param sql + * @param params + * @return + * @throws SQLException + */ + public int updateByModel(String sql, Object model, Object ...params) throws SQLException { + int res = -1; + Connection conn = null; + + try { + conn = dataSourceHolder.getConnection(); + res = jdbcOperations.executeUpdateByModel(conn,sql, model, params); + } finally { + try { + dataSourceHolder.tryClose(conn); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + return res; + } + public int update(SqlAndParams sqlAndParams) throws SQLException { return update(sqlAndParams.getSql(), sqlAndParams.getParamArray()); } @@ -83,7 +104,8 @@ public class JdbcTemplate { } public int updateByModel(String sql, Object model) throws SQLException { - return update(new SqlAndParams(sql, model)); + SqlAndParams sqlAndParams = new SqlAndParams(sql, model); + return updateByModel(sqlAndParams.getSql(), model, sqlAndParams.getParamArray()); } public T query(Class clazz, String sql, Object ...params) throws SQLException { diff --git a/neutrino-proxy-admin/src/views/home/admin/components/DailyTrafficChart.vue b/neutrino-proxy-admin/src/views/home/admin/components/DailyTrafficChart.vue new file mode 100644 index 00000000..55a3ade4 --- /dev/null +++ b/neutrino-proxy-admin/src/views/home/admin/components/DailyTrafficChart.vue @@ -0,0 +1,118 @@ + + + diff --git a/neutrino-proxy-admin/src/views/home/admin/components/LicenseChart.vue b/neutrino-proxy-admin/src/views/home/admin/components/LicenseChart.vue new file mode 100644 index 00000000..2877e055 --- /dev/null +++ b/neutrino-proxy-admin/src/views/home/admin/components/LicenseChart.vue @@ -0,0 +1,111 @@ + + + diff --git a/neutrino-proxy-admin/src/views/home/admin/components/PortMappingChart.vue b/neutrino-proxy-admin/src/views/home/admin/components/PortMappingChart.vue new file mode 100644 index 00000000..d1abe3d9 --- /dev/null +++ b/neutrino-proxy-admin/src/views/home/admin/components/PortMappingChart.vue @@ -0,0 +1,111 @@ + + + diff --git a/neutrino-proxy-admin/src/views/home/admin/components/TableChart.vue b/neutrino-proxy-admin/src/views/home/admin/components/TableChart.vue new file mode 100644 index 00000000..b11e58cb --- /dev/null +++ b/neutrino-proxy-admin/src/views/home/admin/components/TableChart.vue @@ -0,0 +1,39 @@ + + + diff --git a/neutrino-proxy-admin/src/views/home/admin/components/TrafficSumChart.vue b/neutrino-proxy-admin/src/views/home/admin/components/TrafficSumChart.vue new file mode 100644 index 00000000..f9ce735d --- /dev/null +++ b/neutrino-proxy-admin/src/views/home/admin/components/TrafficSumChart.vue @@ -0,0 +1,97 @@ + + + diff --git a/neutrino-proxy-admin/src/views/home/admin/index.copy.vue b/neutrino-proxy-admin/src/views/home/admin/index.copy.vue new file mode 100644 index 00000000..e35aef26 --- /dev/null +++ b/neutrino-proxy-admin/src/views/home/admin/index.copy.vue @@ -0,0 +1,110 @@ + + + + + diff --git a/neutrino-proxy-admin/src/views/home/admin/index.vue b/neutrino-proxy-admin/src/views/home/admin/index.vue index e35aef26..6c5d5f1c 100644 --- a/neutrino-proxy-admin/src/views/home/admin/index.vue +++ b/neutrino-proxy-admin/src/views/home/admin/index.vue @@ -1,110 +1,107 @@ diff --git a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/constant/EnableStatusEnum.java b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/constant/EnableStatusEnum.java index e31229ce..1052739c 100644 --- a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/constant/EnableStatusEnum.java +++ b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/constant/EnableStatusEnum.java @@ -24,6 +24,11 @@ package fun.asgc.neutrino.proxy.server.constant; import lombok.AllArgsConstructor; import lombok.Getter; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + /** * 启用状态枚举 * @author: aoshiguchen @@ -34,7 +39,11 @@ import lombok.Getter; public enum EnableStatusEnum { ENABLE(1, "启用"), DISABLE(2, "禁用"); + private static Map CACHE = Stream.of(EnableStatusEnum.values()).collect(Collectors.toMap(EnableStatusEnum::getStatus, Function.identity())); private Integer status; private String desc; + public static EnableStatusEnum of(Integer status) { + return CACHE.get(status); + } } diff --git a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/constant/ExceptionConstant.java b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/constant/ExceptionConstant.java index c1cc6505..672ac778 100644 --- a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/constant/ExceptionConstant.java +++ b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/constant/ExceptionConstant.java @@ -50,7 +50,7 @@ public enum ExceptionConstant { LOGIN_PASSWORD_NO_CHANGE_MODIFY_FAIL(12004, "密码没有变化,修改失败"), // 端口池管理(13000) PORT_CANNOT_REPEAT(13000,"端口不能重复"), - PORT_NOT_EXIST(13001, "该端口在端口池中不存在,不允许映射"), + PORT_NOT_EXIST(13001, "该端口在端口池中不存在"), // 端口映射管理(14000) PORT_MAPPING_NOT_EXIST(14000, "端口映射记录不存在"), PORT_CANNOT_REPEAT_MAPPING(14001, "服务端口[{}]不能重复映射"), diff --git a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/dal/LicenseMapper.java b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/dal/LicenseMapper.java index 49bcfc27..58546d08 100644 --- a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/dal/LicenseMapper.java +++ b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/dal/LicenseMapper.java @@ -62,6 +62,10 @@ public interface LicenseMapper extends SqlMapper { @Select("select * from license") List listAll(); + @ResultType(LicenseDO.class) + @Select("select * from `license` where user_id = :userId") + List listByUserId(@Param("userId") Integer userId); + /** * 新增license * @param license diff --git a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/dal/PortMappingMapper.java b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/dal/PortMappingMapper.java index da2714ad..c7946b71 100644 --- a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/dal/PortMappingMapper.java +++ b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/dal/PortMappingMapper.java @@ -74,6 +74,14 @@ public interface PortMappingMapper extends SqlMapper { @Select("select * from port_mapping where license_id = ? and enable = 1") List findEnableListByLicenseId(Integer licenseId); + @ResultType(PortMappingDO.class) + @Select("select * from port_mapping where server_port = :serverPort") + List findListByServerPort(@Param("serverPort") Integer serverPort); + + @ResultType(PortMappingDO.class) + @Select("select * from port_mapping where license_id = ?") + List findListByLicenseId(Integer licenseId); + @Update("update `port_mapping` set is_online = :isOnline,update_time = :updateTime where license_id = :licenseId and server_port = :serverPort") void updateOnlineStatus(@Param("licenseId") Integer licenseId, @Param("serverPort") Integer serverPort, @Param("isOnline") Integer isOnline, @Param("updateTime") Date updateTime); diff --git a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/dal/PortPoolMapper.java b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/dal/PortPoolMapper.java index 035aefd9..d3ecbf52 100644 --- a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/dal/PortPoolMapper.java +++ b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/dal/PortPoolMapper.java @@ -62,4 +62,7 @@ public interface PortPoolMapper extends SqlMapper { @Select("select * from port_pool where port = ? limit 0,1") PortPoolDO findByPort(Integer port); + + @Select("select * from port_pool where id = ?") + PortPoolDO findById(Integer id); } diff --git a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/proxy/core/VisitorChannelHandler.java b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/proxy/core/VisitorChannelHandler.java index 481f2326..23f938a0 100755 --- a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/proxy/core/VisitorChannelHandler.java +++ b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/proxy/core/VisitorChannelHandler.java @@ -23,6 +23,7 @@ package fun.asgc.neutrino.proxy.server.proxy.core; import fun.asgc.neutrino.core.util.BeanManager; +import fun.asgc.neutrino.core.util.StringUtil; import fun.asgc.neutrino.proxy.core.Constants; import fun.asgc.neutrino.proxy.core.ProxyMessage; import fun.asgc.neutrino.proxy.server.proxy.domain.VisitorChannelAttachInfo; @@ -87,10 +88,15 @@ public class VisitorChannelHandler extends SimpleChannelInboundHandler } else { String visitorId = newVisitorId(); String lanInfo = ProxyUtil.getClientLanInfoByServerPort(sa.getPort()); - // 用户连接到代理服务器时,设置用户连接不可读,等待代理后端服务器连接成功后再改变为可读状态 - visitorChannel.config().setOption(ChannelOption.AUTO_READ, false); - ProxyUtil.addVisitorChannelToCmdChannel(cmdChannel, visitorId, visitorChannel); - cmdChannel.writeAndFlush(ProxyMessage.buildConnectMessage(visitorId).setData(lanInfo.getBytes())); + if (StringUtil.isEmpty(lanInfo)) { + ctx.channel().close(); + } else { + // 用户连接到代理服务器时,设置用户连接不可读,等待代理后端服务器连接成功后再改变为可读状态 + visitorChannel.config().setOption(ChannelOption.AUTO_READ, false); + + ProxyUtil.addVisitorChannelToCmdChannel(cmdChannel, visitorId, visitorChannel, sa.getPort()); + cmdChannel.writeAndFlush(ProxyMessage.buildConnectMessage(visitorId).setData(lanInfo.getBytes())); + } } super.channelActive(ctx); @@ -133,17 +139,17 @@ public class VisitorChannelHandler extends SimpleChannelInboundHandler public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception { // 通知代理客户端 - Channel userChannel = ctx.channel(); - InetSocketAddress sa = (InetSocketAddress) userChannel.localAddress(); + Channel visitorChannel = ctx.channel(); + InetSocketAddress sa = (InetSocketAddress) visitorChannel.localAddress(); Channel cmdChannel = ProxyUtil.getCmdChannelByServerPort(sa.getPort()); if (cmdChannel == null) { // 该端口还没有代理客户端 ctx.channel().close(); } else { - Channel proxyChannel = userChannel.attr(Constants.NEXT_CHANNEL).get(); + Channel proxyChannel = visitorChannel.attr(Constants.NEXT_CHANNEL).get(); if (proxyChannel != null) { - proxyChannel.config().setOption(ChannelOption.AUTO_READ, userChannel.isWritable()); + proxyChannel.config().setOption(ChannelOption.AUTO_READ, visitorChannel.isWritable()); } } diff --git a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/proxy/domain/ProxyMapping.java b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/proxy/domain/ProxyMapping.java index 46ceedc9..f1134fe8 100644 --- a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/proxy/domain/ProxyMapping.java +++ b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/proxy/domain/ProxyMapping.java @@ -52,11 +52,15 @@ public class ProxyMapping { return list; } for (PortMappingDO portMapping : portMappingList) { - list.add(new ProxyMapping() - .setServerPort(portMapping.getServerPort()) - .setLanInfo(String.format("%s:%s", portMapping.getClientIp(), portMapping.getClientPort()))); + list.add(build(portMapping)); } return list; } + + public static ProxyMapping build(PortMappingDO portMappingDO) { + return new ProxyMapping() + .setServerPort(portMappingDO.getServerPort()) + .setLanInfo(String.format("%s:%s", portMappingDO.getClientIp(), portMappingDO.getClientPort())); + } } diff --git a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/proxy/domain/VisitorChannelAttachInfo.java b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/proxy/domain/VisitorChannelAttachInfo.java index eefa1c05..65c195ae 100644 --- a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/proxy/domain/VisitorChannelAttachInfo.java +++ b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/proxy/domain/VisitorChannelAttachInfo.java @@ -34,6 +34,7 @@ import lombok.experimental.Accessors; public class VisitorChannelAttachInfo { private String visitorId; private String lanInfo; + private Integer serverPort; /** * licenseId */ diff --git a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/proxy/handler/ProxyMessageAuthHandler.java b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/proxy/handler/ProxyMessageAuthHandler.java index 6055ee10..b853610f 100644 --- a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/proxy/handler/ProxyMessageAuthHandler.java +++ b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/proxy/handler/ProxyMessageAuthHandler.java @@ -27,36 +27,24 @@ import fun.asgc.neutrino.core.annotation.Component; import fun.asgc.neutrino.core.annotation.Match; import fun.asgc.neutrino.core.annotation.NonIntercept; import fun.asgc.neutrino.core.util.ChannelUtil; -import fun.asgc.neutrino.core.util.CollectionUtil; import fun.asgc.neutrino.core.util.StringUtil; import fun.asgc.neutrino.proxy.core.*; import fun.asgc.neutrino.proxy.server.base.proxy.ProxyConfig; import fun.asgc.neutrino.proxy.server.constant.ClientConnectTypeEnum; import fun.asgc.neutrino.proxy.server.constant.EnableStatusEnum; +import fun.asgc.neutrino.proxy.server.constant.OnlineStatusEnum; import fun.asgc.neutrino.proxy.server.constant.SuccessCodeEnum; +import fun.asgc.neutrino.proxy.server.dal.LicenseMapper; import fun.asgc.neutrino.proxy.server.dal.entity.ClientConnectRecordDO; import fun.asgc.neutrino.proxy.server.dal.entity.LicenseDO; -import fun.asgc.neutrino.proxy.server.dal.entity.PortMappingDO; import fun.asgc.neutrino.proxy.server.dal.entity.UserDO; -import fun.asgc.neutrino.proxy.server.proxy.core.BytesMetricsHandler; -import fun.asgc.neutrino.proxy.server.proxy.core.VisitorChannelHandler; -import fun.asgc.neutrino.proxy.server.proxy.domain.CmdChannelAttachInfo; -import fun.asgc.neutrino.proxy.server.proxy.domain.ProxyMapping; import fun.asgc.neutrino.proxy.server.service.*; import fun.asgc.neutrino.proxy.server.util.ProxyUtil; -import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.channel.socket.SocketChannel; -import io.netty.channel.socket.nio.NioServerSocketChannel; import lombok.extern.slf4j.Slf4j; -import java.net.BindException; import java.util.Date; -import java.util.List; -import java.util.stream.Collectors; /** * @@ -68,10 +56,6 @@ import java.util.stream.Collectors; @Match(type = Constants.ProxyDataTypeName.AUTH) @Component public class ProxyMessageAuthHandler implements ProxyMessageHandler { - @Autowired("serverBossGroup") - private NioEventLoopGroup serverBossGroup; - @Autowired("serverWorkerGroup") - private NioEventLoopGroup serverWorkerGroup; @Autowired private ProxyConfig proxyConfig; @Autowired @@ -86,6 +70,10 @@ public class ProxyMessageAuthHandler implements ProxyMessageHandler { private FlowReportService flowReportService; @Autowired private ClientConnectRecordService clientConnectRecordService; + @Autowired + private LicenseMapper licenseMapper; + @Autowired + private VisitorChannelService visitorChannelService; @Override public void handle(ChannelHandlerContext ctx, ProxyMessage proxyMessage) { @@ -167,44 +155,14 @@ public class ProxyMessageAuthHandler implements ProxyMessageHandler { .setCode(SuccessCodeEnum.SUCCESS.getCode()) .setCreateTime(now)); - List portMappingList = portMappingService.findEnableListByLicenseId(licenseDO.getId()); - // 没有端口映射仍然保持连接 - if (!CollectionUtil.isEmpty(portMappingList)) { - ProxyUtil.initProxyInfo(licenseDO.getId(), ProxyMapping.buildList(portMappingList)); - - ProxyUtil.addCmdChannel(licenseDO.getId(), ctx.channel(), portMappingList.stream().map(PortMappingDO::getServerPort).collect(Collectors.toSet())); - - startUserPortServer(ProxyUtil.getAttachInfo(ctx.channel()), portMappingList); - } + // 更新license在线状态 + licenseMapper.updateOnlineStatus(licenseDO.getId(), OnlineStatusEnum.ONLINE.getStatus(), now); + // 初始化VisitorChannel + visitorChannelService.initVisitorChannel(licenseDO.getId(), ctx.channel()); } @Override public String name() { return ProxyDataTypeEnum.AUTH.getDesc(); } - - private void startUserPortServer(CmdChannelAttachInfo cmdChannelAttachInfo, List portMappingList) { - ServerBootstrap bootstrap = new ServerBootstrap(); - bootstrap.group(serverBossGroup, serverWorkerGroup) - .channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer() { - @Override - public void initChannel(SocketChannel ch) throws Exception { - ch.pipeline().addFirst(new BytesMetricsHandler()); - ch.pipeline().addLast(new VisitorChannelHandler()); - } - }); - - for (PortMappingDO portMapping : portMappingList) { - try { - proxyMutualService.bindServerPort(cmdChannelAttachInfo, portMapping.getServerPort()); - bootstrap.bind(portMapping.getServerPort()).get(); - log.info("绑定用户端口: {}", portMapping.getServerPort()); - } catch (Exception ex) { - // BindException表示该端口已经绑定过 - if (!(ex.getCause() instanceof BindException)) { - throw new RuntimeException(ex); - } - } - } - } } diff --git a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/service/LicenseService.java b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/service/LicenseService.java index 20eaaee3..f19134a1 100644 --- a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/service/LicenseService.java +++ b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/service/LicenseService.java @@ -58,6 +58,8 @@ public class LicenseService { private LicenseMapper licenseMapper; @Autowired private UserMapper userMapper; + @Autowired + private VisitorChannelService visitorChannelService; public Page page(PageQuery pageQuery, LicenseListReq req) { Page page = Page.create(pageQuery); @@ -159,7 +161,8 @@ public class LicenseService { */ public LicenseUpdateEnableStatusRes updateEnableStatus(LicenseUpdateEnableStatusReq req) { licenseMapper.updateEnableStatus(req.getId(), req.getEnable(), new Date()); - + // 更新VisitorChannel + visitorChannelService.updateVisitorChannelByLicenseId(req.getId(), req.getEnable()); return new LicenseUpdateEnableStatusRes(); } @@ -169,6 +172,8 @@ public class LicenseService { */ public void delete(Integer id) { licenseMapper.delete(id); + // 更新VisitorChannel + visitorChannelService.updateVisitorChannelByLicenseId(id, EnableStatusEnum.DISABLE.getStatus()); } /** diff --git a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/service/PortMappingService.java b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/service/PortMappingService.java index 15adbeb1..8c08eaa8 100644 --- a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/service/PortMappingService.java +++ b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/service/PortMappingService.java @@ -68,6 +68,8 @@ public class PortMappingService { private UserMapper userMapper; @Autowired private PortPoolMapper portPoolMapper; + @Autowired + private VisitorChannelService visitorChannelService; public Page page(PageQuery pageQuery, PortMappingListReq req) { Page page = Page.create(pageQuery); @@ -126,6 +128,8 @@ public class PortMappingService { portMappingDO.setCreateTime(now); portMappingDO.setUpdateTime(now); portMappingMapper.add(portMappingDO); + // 更新VisitorChannel + visitorChannelService.addVisitorChannelByPortMapping(portMappingDO); return new PortMappingCreateRes(); } @@ -140,6 +144,10 @@ public class PortMappingService { ParamCheckUtil.checkNotNull(portPoolDO, ExceptionConstant.PORT_NOT_EXIST); ParamCheckUtil.checkExpression(null == portMappingMapper.findByPort(req.getServerPort(), Sets.newHashSet(req.getId())), ExceptionConstant.PORT_CANNOT_REPEAT_MAPPING, req.getServerPort()); + // 查询原端口映射 + PortMappingDO oldPortMappingDO = portMappingMapper.findById(req.getId()); + ParamCheckUtil.checkNotNull(oldPortMappingDO, ExceptionConstant.PORT_MAPPING_NOT_EXIST); + PortMappingDO portMappingDO = new PortMappingDO(); portMappingDO.setId(req.getId()); portMappingDO.setLicenseId(req.getLicenseId()); @@ -147,7 +155,10 @@ public class PortMappingService { portMappingDO.setClientIp(req.getClientIp()); portMappingDO.setClientPort(req.getClientPort()); portMappingDO.setUpdateTime(new Date()); + portMappingDO.setEnable(EnableStatusEnum.ENABLE.getStatus()); portMappingMapper.update(portMappingDO); + // 更新VisitorChannel + visitorChannelService.updateVisitorChannelByPortMapping(oldPortMappingDO, portMappingDO); return new PortMappingUpdateRes(); } @@ -193,6 +204,14 @@ public class PortMappingService { portMappingMapper.updateEnableStatus(req.getId(), req.getEnable(), new Date()); + // 更新VisitorChannel + portMappingDO.setEnable(req.getEnable()); + if (EnableStatusEnum.ENABLE == EnableStatusEnum.of(req.getEnable())) { + visitorChannelService.addVisitorChannelByPortMapping(portMappingDO); + } else { + visitorChannelService.removeVisitorChannelByPortMapping(portMappingDO); + } + return new PortMappingUpdateEnableStatusRes(); } @@ -207,6 +226,9 @@ public class PortMappingService { } portMappingMapper.delete(id); + + // 更新VisitorChannel + visitorChannelService.removeVisitorChannelByPortMapping(portMappingDO); } /** diff --git a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/service/PortPoolService.java b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/service/PortPoolService.java index 5fef3d10..53257fdb 100644 --- a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/service/PortPoolService.java +++ b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/service/PortPoolService.java @@ -52,6 +52,8 @@ public class PortPoolService { @Autowired private PortPoolMapper portPoolMapper; + @Autowired + private VisitorChannelService visitorChannelService; public Page page(PageQuery pageQuery, PortPoolListReq req) { Page page = Page.create(pageQuery); @@ -65,7 +67,7 @@ public class PortPoolService { public PortPoolCreateRes create(PortPoolCreateReq req) { PortPoolDO oldPortPoolDO = portPoolMapper.findByPort(req.getPort()); - ParamCheckUtil.checkNotNull(oldPortPoolDO, ExceptionConstant.PORT_CANNOT_REPEAT); + ParamCheckUtil.checkMustNull(oldPortPoolDO, ExceptionConstant.PORT_CANNOT_REPEAT); Date now = new Date(); @@ -75,19 +77,31 @@ public class PortPoolService { .setCreateTime(now) .setUpdateTime(now) ); + // 更新visitorChannel + visitorChannelService.updateVisitorChannelByPortPool(req.getPort(), EnableStatusEnum.ENABLE.getStatus()); return new PortPoolCreateRes(); } public PortPoolUpdateEnableStatusRes updateEnableStatus(PortPoolUpdateEnableStatusReq req) { - + PortPoolDO portPoolDO = portPoolMapper.findById(req.getId()); + ParamCheckUtil.checkNotNull(portPoolDO, ExceptionConstant.PORT_NOT_EXIST); portPoolMapper.updateEnableStatus(req.getId(), req.getEnable(), new Date()); + // 更新visitorChannel + visitorChannelService.updateVisitorChannelByPortPool(portPoolDO.getPort(), req.getEnable()); + return new PortPoolUpdateEnableStatusRes(); } public void delete(Integer id) { + PortPoolDO portPoolDO = portPoolMapper.findById(id); + ParamCheckUtil.checkNotNull(portPoolDO, ExceptionConstant.PORT_NOT_EXIST); + portPoolMapper.delete(id); + + // 更新visitorChannel + visitorChannelService.updateVisitorChannelByPortPool(portPoolDO.getPort(), EnableStatusEnum.DISABLE.getStatus()); } } diff --git a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/service/UserService.java b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/service/UserService.java index 4b8dbae5..fa034c11 100644 --- a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/service/UserService.java +++ b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/service/UserService.java @@ -62,6 +62,8 @@ public class UserService { private UserTokenMapper userTokenMapper; @Autowired private UserLoginRecordMapper userLoginRecordMapper; + @Autowired + private VisitorChannelService visitorChannelService; public LoginRes login(LoginReq req) { UserDO userDO = userMapper.findByLoginName(req.getLoginName()); @@ -162,7 +164,8 @@ public class UserService { public UserUpdateEnableStatusRes updateEnableStatus(UserUpdateEnableStatusReq req) { userMapper.updateEnableStatus(req.getId(), req.getEnable(), new Date()); - + // 更新VisitorChannel + visitorChannelService.updateVisitorChannelByUserId(req.getId(), req.getEnable()); return new UserUpdateEnableStatusRes(); } @@ -208,5 +211,7 @@ public class UserService { public void delete(Integer id) { userMapper.delete(id); + // 更新VisitorChannel + visitorChannelService.updateVisitorChannelByUserId(id, EnableStatusEnum.DISABLE.getStatus()); } } diff --git a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/service/VisitorChannelService.java b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/service/VisitorChannelService.java new file mode 100644 index 00000000..1d2013cb --- /dev/null +++ b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/service/VisitorChannelService.java @@ -0,0 +1,266 @@ +/** + * Copyright (c) 2022 aoshiguchen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package fun.asgc.neutrino.proxy.server.service; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import fun.asgc.neutrino.core.annotation.Autowired; +import fun.asgc.neutrino.core.annotation.Component; +import fun.asgc.neutrino.core.annotation.NonIntercept; +import fun.asgc.neutrino.core.util.CollectionUtil; +import fun.asgc.neutrino.proxy.core.Constants; +import fun.asgc.neutrino.proxy.server.constant.EnableStatusEnum; +import fun.asgc.neutrino.proxy.server.dal.LicenseMapper; +import fun.asgc.neutrino.proxy.server.dal.PortMappingMapper; +import fun.asgc.neutrino.proxy.server.dal.PortPoolMapper; +import fun.asgc.neutrino.proxy.server.dal.UserMapper; +import fun.asgc.neutrino.proxy.server.dal.entity.LicenseDO; +import fun.asgc.neutrino.proxy.server.dal.entity.PortMappingDO; +import fun.asgc.neutrino.proxy.server.dal.entity.PortPoolDO; +import fun.asgc.neutrino.proxy.server.dal.entity.UserDO; +import fun.asgc.neutrino.proxy.server.proxy.core.BytesMetricsHandler; +import fun.asgc.neutrino.proxy.server.proxy.core.VisitorChannelHandler; +import fun.asgc.neutrino.proxy.server.proxy.domain.CmdChannelAttachInfo; +import fun.asgc.neutrino.proxy.server.proxy.domain.ProxyMapping; +import fun.asgc.neutrino.proxy.server.util.ProxyUtil; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.Channel; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import lombok.extern.slf4j.Slf4j; + +import java.net.BindException; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 访问者通道服务 + * @author: aoshiguchen + * @date: 2023/2/5 + */ +@Slf4j +@NonIntercept +@Component +public class VisitorChannelService { + @Autowired("serverBossGroup") + private NioEventLoopGroup serverBossGroup; + @Autowired("serverWorkerGroup") + private NioEventLoopGroup serverWorkerGroup; + @Autowired + private ProxyMutualService proxyMutualService; + @Autowired + private UserMapper userMapper; + @Autowired + private LicenseMapper licenseMapper; + @Autowired + private PortMappingMapper portMappingMapper; + @Autowired + private PortPoolMapper portPoolMapper; + + /** + * 初始化 + * @param licenseId + */ + public void initVisitorChannel(Integer licenseId, Channel cmdChannel) { + List portMappingList = portMappingMapper.findEnableListByLicenseId(licenseId); + // 没有端口映射仍然保持连接 + ProxyUtil.initProxyInfo(licenseId, ProxyMapping.buildList(portMappingList)); + + ProxyUtil.addCmdChannel(licenseId, cmdChannel, portMappingList.stream().map(PortMappingDO::getServerPort).collect(Collectors.toSet())); + + startUserPortServer(ProxyUtil.getAttachInfo(cmdChannel), portMappingList); + } + + /** + * 更新 + * 触发时机:删除端口池、禁用端口池、启用端口池 + * @param serverPort + * @param enable + */ + public void updateVisitorChannelByPortPool(Integer serverPort, Integer enable) { + if (null == serverPort) { + return; + } + List portMappingDOList = portMappingMapper.findListByServerPort(serverPort); + if (CollectionUtil.isEmpty(portMappingDOList)) { + return; + } + EnableStatusEnum enableStatusEnum = EnableStatusEnum.of(enable); + for (PortMappingDO portMappingDO : portMappingDOList) { + if (EnableStatusEnum.DISABLE == enableStatusEnum) { + removeVisitorChannelByPortMapping(portMappingDO); + } else if (EnableStatusEnum.ENABLE == EnableStatusEnum.of(portMappingDO.getEnable())) { + addVisitorChannelByPortMapping(portMappingDO); + } + } + } + + /** + * 更新 + * 触发时机:删除用户、禁用用户、启用用户 (新增、修改用户不涉及VisitorChannel的变更) + * @param userId + */ + public void updateVisitorChannelByUserId(Integer userId, Integer enable) { + if (null == userId) { + return; + } + List licenseDOList = licenseMapper.listByUserId(userId); + if (CollectionUtil.isEmpty(licenseDOList)) { + return; + } + for (LicenseDO licenseDO : licenseDOList) { + updateVisitorChannelByLicenseId(licenseDO.getId(), enable); + } + } + + /** + * 更新 + * 触发时机:删除license、禁用license、启用license (新增、修改license不涉及VisitorChannel的变更) + * 重置licenseKey,不会立即影响已经连接成功的license,如果想要立即影响,请先进行禁用 + * @param licenseId + */ + public void updateVisitorChannelByLicenseId(Integer licenseId, Integer enable) { + if (null == licenseId) { + return; + } + Channel cmdChannel = ProxyUtil.getCmdChannelByLicenseId(licenseId); + if (null == cmdChannel) { + // 如果不存在有效的cmdChannel,则无需更新VisitorChannel + return; + } + EnableStatusEnum enableStatusEnum = EnableStatusEnum.of(enable); + List portMappingDOList = portMappingMapper.findListByLicenseId(licenseId); + if (!CollectionUtil.isEmpty(portMappingDOList)) { + for (PortMappingDO portMappingDO : portMappingDOList) { + if (EnableStatusEnum.DISABLE == enableStatusEnum) { + removeVisitorChannelByPortMapping(portMappingDO); + } else if (EnableStatusEnum.ENABLE == EnableStatusEnum.of(portMappingDO.getEnable())) { + addVisitorChannelByPortMapping(portMappingDO); + } + } + } + } + + /** + * 更新 + * 触发时机:修改端口映射 + * @param oldPortMappingDO + * @param newPortMappingDO + */ + public void updateVisitorChannelByPortMapping(PortMappingDO oldPortMappingDO, PortMappingDO newPortMappingDO) { + if (null == oldPortMappingDO || null == newPortMappingDO) { + return; + } + removeVisitorChannelByPortMapping(oldPortMappingDO); + addVisitorChannelByPortMapping(newPortMappingDO); + } + + /** + * 新增VisitorChannel + * 触发时机:新增端口映射、启用端口映射 + * @param portMappingDO + */ + public void addVisitorChannelByPortMapping(PortMappingDO portMappingDO) { + if (null == portMappingDO) { + return; + } + Channel cmdChannel = ProxyUtil.getCmdChannelByLicenseId(portMappingDO.getLicenseId()); + if (null == cmdChannel) { + // 如果不存在有效的cmdChannel,则无需更新VisitorChannel + return; + } + // 判断端口映射是否启用 + if (EnableStatusEnum.DISABLE != EnableStatusEnum.of(portMappingDO.getEnable())) { + LicenseDO licenseDO = licenseMapper.findById(portMappingDO.getLicenseId()); + // 判断license是否启用 + if (null != licenseDO && EnableStatusEnum.ENABLE == EnableStatusEnum.of(licenseDO.getEnable())) { + UserDO userDO = userMapper.findById(licenseDO.getUserId()); + // 判断用户是否启用 + if (null != userDO && EnableStatusEnum.ENABLE == EnableStatusEnum.of(userDO.getEnable())) { + PortPoolDO portPoolDO = portPoolMapper.findByPort(portMappingDO.getServerPort()); + // 判断端口池是否启用 + if (null != portPoolDO && EnableStatusEnum.ENABLE == EnableStatusEnum.of(portPoolDO.getEnable())) { + // 未删除且未禁用,则开启代理 + ProxyUtil.addProxyInfo(portMappingDO.getLicenseId(), ProxyMapping.build(portMappingDO)); + ProxyUtil.addCmdChannel(portMappingDO.getLicenseId(), cmdChannel, Sets.newHashSet(portMappingDO.getServerPort())); + startUserPortServer(ProxyUtil.getAttachInfo(cmdChannel), Lists.newArrayList(portMappingDO)); + } + } + } + } + } + + /** + * 删除VisitorChannel + * 触发时机:删除端口映射、禁用端口映射 + * @param portMappingDO + */ + public void removeVisitorChannelByPortMapping(PortMappingDO portMappingDO) { + if (null == portMappingDO) { + return; + } + Channel cmdChannel = ProxyUtil.getCmdChannelByLicenseId(portMappingDO.getLicenseId()); + if (null == cmdChannel) { + // 如果不存在有效的cmdChannel,则无需更新VisitorChannel + return; + } + Channel visitorChannel = ProxyUtil.getVisitorChannelByServerPort(portMappingDO.getServerPort()); + if (null != visitorChannel) { + Channel proxyChannel = visitorChannel.attr(Constants.NEXT_CHANNEL).get(); + if (null != proxyChannel) { + proxyChannel.close(); + } + visitorChannel.close(); + } + ProxyUtil.removeProxyInfo(portMappingDO.getServerPort()); + } + + private void startUserPortServer(CmdChannelAttachInfo cmdChannelAttachInfo, List portMappingList) { + if (CollectionUtil.isEmpty(portMappingList)) { + return; + } + ServerBootstrap bootstrap = new ServerBootstrap(); + bootstrap.group(serverBossGroup, serverWorkerGroup) + .channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer() { + @Override + public void initChannel(SocketChannel ch) throws Exception { + ch.pipeline().addFirst(new BytesMetricsHandler()); + ch.pipeline().addLast(new VisitorChannelHandler()); + } + }); + + for (PortMappingDO portMapping : portMappingList) { + try { + proxyMutualService.bindServerPort(cmdChannelAttachInfo, portMapping.getServerPort()); + bootstrap.bind(portMapping.getServerPort()).get(); + log.info("绑定用户端口: {}", portMapping.getServerPort()); + } catch (Exception ex) { + // BindException表示该端口已经绑定过 + if (!(ex.getCause() instanceof BindException)) { + throw new RuntimeException(ex); + } + } + } + } +} diff --git a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/util/ProxyUtil.java b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/util/ProxyUtil.java index 531f2dad..4d900d8a 100644 --- a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/util/ProxyUtil.java +++ b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/util/ProxyUtil.java @@ -21,6 +21,7 @@ */ package fun.asgc.neutrino.proxy.server.util; +import com.google.common.collect.Sets; import fun.asgc.neutrino.core.util.ChannelUtil; import fun.asgc.neutrino.core.util.CollectionUtil; import fun.asgc.neutrino.proxy.core.ChannelAttribute; @@ -50,7 +51,7 @@ public class ProxyUtil { /** * 代理信息映射 */ - private static final Map proxyInfoMap = new HashMap<>(); + private static final Map proxyInfoMap = new ConcurrentHashMap<>(); /** * 服务端口 -> 指令通道映射 */ @@ -59,6 +60,10 @@ public class ProxyUtil { * license -> 指令通道映射 */ private static Map licenseToCmdChannelMap = new ConcurrentHashMap<>(); + /** + * 服务端口 -> 访问通道映射 + */ + private static Map serverPortToVisitorChannel = new ConcurrentHashMap<>(); /** * cmdChannelAttachInfo.getUserChannelMap() 读写锁 @@ -72,12 +77,30 @@ public class ProxyUtil { */ public static void initProxyInfo(Integer licenseId, List proxyMappingList) { licenseToServerPortMap.put(licenseId, new HashSet<>()); - for (ProxyMapping proxyMapping : proxyMappingList) { - licenseToServerPortMap.get(licenseId).add(proxyMapping.getServerPort()); - proxyInfoMap.put(proxyMapping.getServerPort(), proxyMapping.getLanInfo()); + addProxyInfo(licenseId, proxyMappingList); + } + + public static void addProxyInfo(Integer licenseId, List proxyMappingList) { + if (!CollectionUtil.isEmpty(proxyMappingList)) { + for (ProxyMapping proxyMapping : proxyMappingList) { + licenseToServerPortMap.get(licenseId).add(proxyMapping.getServerPort()); + proxyInfoMap.put(proxyMapping.getServerPort(), proxyMapping.getLanInfo()); + } } } + public static void addProxyInfo(Integer licenseId, ProxyMapping proxyMapping) { + if (null == licenseId || null == proxyMapping) { + return; + } + licenseToServerPortMap.get(licenseId).add(proxyMapping.getServerPort()); + proxyInfoMap.put(proxyMapping.getServerPort(), proxyMapping.getLanInfo()); + } + + public static void removeProxyInfo(Integer serverPort) { + proxyInfoMap.remove(serverPort); + } + /** * 根据licenseId获取服务端端口集合 * @param licenseId licenseId @@ -103,19 +126,25 @@ public class ProxyUtil { * @param serverPorts 服务端端口集合 */ public static void addCmdChannel(Integer licenseId, Channel cmdChannel, Set serverPorts) { - if (CollectionUtil.isEmpty(serverPorts)) { - return; + if (!CollectionUtil.isEmpty(serverPorts)) { + for (int port : serverPorts) { + serverPortToCmdChannelMap.put(port, cmdChannel); + } + } + CmdChannelAttachInfo cmdChannelAttachInfo = getAttachInfo(cmdChannel); + if (null == cmdChannelAttachInfo) { + cmdChannelAttachInfo = new CmdChannelAttachInfo() + .setIp(ChannelUtil.getIP(cmdChannel)) + .setLicenseId(licenseId) + .setVisitorChannelMap(new HashMap<>(16)) + .setServerPorts(Sets.newHashSet()); + setAttachInfo(cmdChannel, cmdChannelAttachInfo); } - for (int port : serverPorts) { - serverPortToCmdChannelMap.put(port, cmdChannel); + if (!CollectionUtil.isEmpty(serverPorts)) { + cmdChannelAttachInfo.getServerPorts().addAll(serverPorts); } - setAttachInfo(cmdChannel, new CmdChannelAttachInfo() - .setIp(ChannelUtil.getIP(cmdChannel)) - .setServerPorts(serverPorts) - .setLicenseId(licenseId) - .setVisitorChannelMap(new HashMap<>(16))); licenseToCmdChannelMap.put(licenseId, cmdChannel); } @@ -174,7 +203,7 @@ public class ProxyUtil { * @param visitorId * @param visitorChannel */ - public static void addVisitorChannelToCmdChannel(Channel cmdChannel, String visitorId, Channel visitorChannel) { + public static void addVisitorChannelToCmdChannel(Channel cmdChannel, String visitorId, Channel visitorChannel, Integer serverPort) { InetSocketAddress sa = (InetSocketAddress) visitorChannel.localAddress(); String lanInfo = getClientLanInfoByServerPort(sa.getPort()); CmdChannelAttachInfo cmdChannelAttachInfo = getAttachInfo(cmdChannel); @@ -182,6 +211,7 @@ public class ProxyUtil { setAttachInfo(visitorChannel, new VisitorChannelAttachInfo() .setVisitorId(visitorId) .setLanInfo(lanInfo) + .setServerPort(serverPort) .setLicenseId(cmdChannelAttachInfo.getLicenseId()) .setIp(ChannelUtil.getIP(visitorChannel)) ); @@ -191,6 +221,7 @@ public class ProxyUtil { } finally { userChannelMapLock.writeLock().unlock(); } + serverPortToVisitorChannel.put(serverPort, visitorChannel); } public static Channel removeVisitorChannelFromCmdChannel(Channel cmdChannel, String visitorId) { @@ -219,6 +250,15 @@ public class ProxyUtil { return ((CmdChannelAttachInfo)getAttachInfo(cmdChannel)).getVisitorChannelMap().get(visitorId); } + /** + * 根据服务端口获取访问通道 + * @param serverPort + * @return + */ + public static Channel getVisitorChannelByServerPort(Integer serverPort) { + return serverPortToVisitorChannel.get(serverPort); + } + /** * 获取访问者ID * diff --git a/todolist.MD b/todolist.MD index f38ef29c..087c2614 100644 --- a/todolist.MD +++ b/todolist.MD @@ -6,7 +6,31 @@ - 弹框展示月度明细 - 弹框展示今日流量明细 - 首页图表📈 - + - 1、License在线数 + - 2、端口映射在线数 + - 3、今日流量(上行、下行) + - 4、历史流量(上行、下行) + - 点击1~4 切换列表 + - 在线License列表 + - 用户名 + - License名称 + - LicenseKey + - 在线端口映射列表 + - 用户名 + - License名称 + - 服务端口 + - 代理客户端 + - 今日24小时流量列表(按小时到排序) + - 用户名 + - License名称 + - 时间 + - 流量 + - 历史流量列表(取最近12个月按月到排序) + - 用户名 + - License名称 + - 时间 + - 流量 + - 今日流量折线图(上行、下行、总流量,按分钟统计0~24小时) # Bug - windows环境下直接运行发布版的jar包,日志输出乱码 - 部份用户windows环境下启动客户端,扫描类个数为0个