客户端设置license逻辑优化
This commit is contained in:
@@ -2,6 +2,8 @@
|
||||
# Compiled class file
|
||||
*.class
|
||||
data.db*
|
||||
.neutrino-proxy.license
|
||||
|
||||
|
||||
# Log file
|
||||
*.log
|
||||
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"environment": "我的Mac",
|
||||
"clientKey": "79419a1a8691413aa5e845b9e3e90051",
|
||||
"proxy": [
|
||||
{
|
||||
"serverPort": 9100,
|
||||
"clientInfo": "127.0.0.1:3306"
|
||||
},
|
||||
{
|
||||
"serverPort": 9101,
|
||||
"clientInfo": "rm-uf63ggkfh7zrr08vw.mysql.rds.aliyuncs.com:3306"
|
||||
},
|
||||
{
|
||||
"serverPort": 9102,
|
||||
"clientInfo": "127.0.0.1:8080"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -99,7 +99,7 @@ public class DefaultDispatcher<Context, Data> implements Dispatcher<Context, Dat
|
||||
}
|
||||
Handler<Context,Data> handler = handlerMap.get(type);
|
||||
if (null == handler) {
|
||||
log.warn("{} 找不到匹配的处理器 type:{}", this.name, type);
|
||||
// log.debug("{} 找不到匹配的处理器 type:{}", this.name, type);
|
||||
return;
|
||||
}
|
||||
String handlerName = handler.name();
|
||||
|
||||
@@ -30,5 +30,4 @@ package fun.asgc.neutrino.core.context;
|
||||
public interface ApplicationRunner {
|
||||
|
||||
void run(String[] args) throws Exception;
|
||||
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
package fun.asgc.neutrino.core.context;
|
||||
|
||||
import fun.asgc.neutrino.core.util.SystemUtil;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
@@ -59,4 +60,8 @@ public class Environment {
|
||||
* 启用job
|
||||
*/
|
||||
private boolean enableJob;
|
||||
/**
|
||||
* 运行上下文
|
||||
*/
|
||||
private SystemUtil.RunContext runContext;
|
||||
}
|
||||
|
||||
@@ -61,11 +61,13 @@ public class NeutrinoLauncher {
|
||||
|
||||
environmentInit();
|
||||
ApplicationContext context = new ApplicationContext(environment);
|
||||
context.run();
|
||||
SystemUtil.RunContext runContext = SystemUtil.waitProcessDestroy(() -> {
|
||||
context.destroy();
|
||||
log.info("Application already stop.");
|
||||
});
|
||||
environment.setRunContext(runContext);
|
||||
|
||||
context.run();
|
||||
|
||||
stopWatch.stop();
|
||||
printLog(environment, stopWatch);
|
||||
|
||||
@@ -112,4 +112,12 @@ public class SystemUtil {
|
||||
String osName = System.getProperty("os.name", "unknown");
|
||||
return osName.toLowerCase().indexOf("windows") != -1;
|
||||
}
|
||||
|
||||
public static void trySleep(long millis) {
|
||||
try {
|
||||
Thread.sleep(millis);
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
@@ -39,6 +39,7 @@ public class ProxyConfig {
|
||||
private Protocol protocol;
|
||||
private Client client;
|
||||
private String licenseKey;
|
||||
public static volatile boolean authSuccess;
|
||||
|
||||
@Data
|
||||
public static class Protocol {
|
||||
@@ -72,6 +73,8 @@ public class ProxyConfig {
|
||||
private Integer serverPort;
|
||||
@Value("ssl-enable")
|
||||
private Boolean sslEnable;
|
||||
@Value("obtain-license-interval")
|
||||
private Integer obtainLicenseInterval;
|
||||
}
|
||||
|
||||
@Init
|
||||
|
||||
+3
-1
@@ -59,7 +59,9 @@ public class ClientChannelHandler extends SimpleChannelInboundHandler<ProxyMessa
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, ProxyMessage proxyMessage) throws Exception {
|
||||
log.info("recieved proxy message, type is {}", proxyMessage.getType());
|
||||
if (ProxyMessage.TYPE_HEARTBEAT != proxyMessage.getType()) {
|
||||
log.info("recieved proxy message, type is {}", proxyMessage.getType());
|
||||
}
|
||||
dispatcher.dispatch(ctx, proxyMessage);
|
||||
}
|
||||
|
||||
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
/**
|
||||
* 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.client.core;
|
||||
|
||||
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.base.CustomThreadFactory;
|
||||
import fun.asgc.neutrino.core.context.ApplicationRunner;
|
||||
import fun.asgc.neutrino.core.util.ArrayUtil;
|
||||
import fun.asgc.neutrino.core.util.FileUtil;
|
||||
import fun.asgc.neutrino.core.util.StringUtil;
|
||||
import fun.asgc.neutrino.core.util.SystemUtil;
|
||||
import fun.asgc.neutrino.proxy.client.config.ProxyConfig;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.Scanner;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author: aoshiguchen
|
||||
* @date: 2022/9/4
|
||||
*/
|
||||
@Slf4j
|
||||
@NonIntercept
|
||||
@Component
|
||||
public class LicenseObtainService implements ApplicationRunner {
|
||||
@Autowired
|
||||
private ProxyConfig proxyConfig;
|
||||
/**
|
||||
* 调度器
|
||||
*/
|
||||
private static final ScheduledExecutorService scheduledExecutor = Executors.newSingleThreadScheduledExecutor(new CustomThreadFactory("LicenseObtain"));
|
||||
|
||||
@Autowired
|
||||
private ProxyClientRunner proxyClientRunner;
|
||||
private ReentrantLock runLock = new ReentrantLock();
|
||||
private Scanner scanner = new Scanner(System.in);
|
||||
private volatile boolean isFirst;
|
||||
|
||||
@Override
|
||||
public void run(String[] args) throws Exception {
|
||||
isFirst = true;
|
||||
scheduledExecutor.scheduleWithFixedDelay(() -> {
|
||||
boolean lock = runLock.tryLock();
|
||||
try {
|
||||
if (lock) {
|
||||
this.process(args);
|
||||
}
|
||||
} finally {
|
||||
if (runLock.isHeldByCurrentThread()){
|
||||
runLock.unlock();
|
||||
}
|
||||
}
|
||||
}, 0, proxyConfig.getClient().getObtainLicenseInterval(), TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
scheduledExecutor.shutdown();
|
||||
log.info("licenseKey获取任务停止");
|
||||
}
|
||||
|
||||
public void process(String[] args) {
|
||||
if (isFirst) {
|
||||
isFirst = true;
|
||||
SystemUtil.trySleep(2000);
|
||||
}
|
||||
String licenseKey = getLicenseKey(args);
|
||||
proxyClientRunner.start(licenseKey);
|
||||
}
|
||||
|
||||
private String getLicenseKey(String[] args) {
|
||||
String license = "";
|
||||
if (null != args && ArrayUtil.notEmpty(args)) {
|
||||
for (String s : args) {
|
||||
if (s.startsWith("license=") && s.length() > 8) {
|
||||
license = s.substring(8).trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (StringUtil.isEmpty(license)) {
|
||||
license = FileUtil.readContentAsString("./.neutrino-proxy.license");
|
||||
}
|
||||
|
||||
while (StringUtil.isEmpty(license)) {
|
||||
System.out.print("请输入license:");
|
||||
license = scanner.next();
|
||||
}
|
||||
|
||||
return license;
|
||||
}
|
||||
}
|
||||
+18
-31
@@ -27,17 +27,14 @@ import fun.asgc.neutrino.core.annotation.Bean;
|
||||
import fun.asgc.neutrino.core.annotation.Component;
|
||||
import fun.asgc.neutrino.core.annotation.NonIntercept;
|
||||
import fun.asgc.neutrino.core.context.ApplicationRunner;
|
||||
import fun.asgc.neutrino.core.util.ArrayUtil;
|
||||
import fun.asgc.neutrino.core.context.Environment;
|
||||
import fun.asgc.neutrino.core.util.FileUtil;
|
||||
import fun.asgc.neutrino.core.util.StringUtil;
|
||||
import fun.asgc.neutrino.proxy.client.config.ProxyConfig;
|
||||
import fun.asgc.neutrino.proxy.client.util.ProxyUtil;
|
||||
import fun.asgc.neutrino.proxy.core.*;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.*;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
@@ -51,6 +48,7 @@ import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import java.io.InputStream;
|
||||
import java.security.KeyStore;
|
||||
import java.util.Scanner;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -60,7 +58,7 @@ import java.security.KeyStore;
|
||||
@Slf4j
|
||||
@NonIntercept
|
||||
@Component
|
||||
public class ProxyClientRunner implements ApplicationRunner {
|
||||
public class ProxyClientRunner {
|
||||
@Autowired
|
||||
private ProxyConfig proxyConfig;
|
||||
@Autowired("bootstrap")
|
||||
@@ -68,11 +66,20 @@ public class ProxyClientRunner implements ApplicationRunner {
|
||||
@Autowired("realServerBootstrap")
|
||||
private static Bootstrap realServerBootstrap;
|
||||
private static NioEventLoopGroup workerGroup;
|
||||
@Autowired
|
||||
private Environment environment;
|
||||
private volatile Channel channel;
|
||||
|
||||
@Override
|
||||
public void run(String[] args) {
|
||||
proxyConfig.setLicenseKey(getLicenseKey(args));
|
||||
connectProxyServer();
|
||||
public void start(String licenseKey) {
|
||||
if (StringUtil.isEmpty(licenseKey)) {
|
||||
return;
|
||||
}
|
||||
proxyConfig.setLicenseKey(licenseKey);
|
||||
if (null == channel || !channel.isActive()) {
|
||||
connectProxyServer();
|
||||
} else {
|
||||
channel.writeAndFlush(ProxyMessage.buildAuthMessage(proxyConfig.getLicenseKey()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -114,6 +121,7 @@ public class ProxyClientRunner implements ApplicationRunner {
|
||||
@Override
|
||||
public void operationComplete(ChannelFuture future) throws Exception {
|
||||
if (future.isSuccess()) {
|
||||
channel = future.channel();
|
||||
// 连接成功,向服务器发送客户端认证信息(licenseKey)
|
||||
ProxyUtil.setCmdChannel(future.channel());
|
||||
future.channel().writeAndFlush(ProxyMessage.buildAuthMessage(proxyConfig.getLicenseKey()));
|
||||
@@ -158,25 +166,4 @@ public class ProxyClientRunner implements ApplicationRunner {
|
||||
public Bootstrap realServerBootstrap() {
|
||||
return new Bootstrap();
|
||||
}
|
||||
|
||||
private String getLicenseKey(String[] args) {
|
||||
String license = "";
|
||||
if (null != args && ArrayUtil.notEmpty(args)) {
|
||||
for (String s : args) {
|
||||
if (s.startsWith("license=") && s.length() > 8) {
|
||||
license = s.substring(8).trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (StringUtil.isEmpty(license)) {
|
||||
license = FileUtil.readContentAsString("./.neutrino-proxy.license");
|
||||
}
|
||||
if (StringUtil.isEmpty(license)) {
|
||||
log.error("未配置license,执行结束.");
|
||||
System.exit(-1);
|
||||
}
|
||||
FileUtil.write("./.neutrino-proxy.license", license);
|
||||
|
||||
return license;
|
||||
}
|
||||
}
|
||||
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* 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.client.handler;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import fun.asgc.neutrino.core.annotation.Autowired;
|
||||
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.FileUtil;
|
||||
import fun.asgc.neutrino.proxy.client.config.ProxyConfig;
|
||||
import fun.asgc.neutrino.proxy.client.core.LicenseObtainService;
|
||||
import fun.asgc.neutrino.proxy.core.Constants;
|
||||
import fun.asgc.neutrino.proxy.core.ExceptionEnum;
|
||||
import fun.asgc.neutrino.proxy.core.ProxyMessage;
|
||||
import fun.asgc.neutrino.proxy.core.ProxyMessageHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author: aoshiguchen
|
||||
* @date: 2022/9/4
|
||||
*/
|
||||
@Slf4j
|
||||
@NonIntercept
|
||||
@Match(type = Constants.ProxyDataTypeName.AUTH)
|
||||
@Component
|
||||
public class ProxyMessageAuthHandler implements ProxyMessageHandler {
|
||||
@Autowired
|
||||
private LicenseObtainService licenseObtainService;
|
||||
|
||||
@Override
|
||||
public void handle(ChannelHandlerContext context, ProxyMessage proxyMessage) {
|
||||
String info = proxyMessage.getInfo();
|
||||
JSONObject data = JSONObject.parseObject(info);
|
||||
Integer code = data.getInteger("code");
|
||||
String licenseKey = data.getString("licenseKey");
|
||||
log.info("认证结果:{}", info);
|
||||
if (ExceptionEnum.SUCCESS.getCode().equals(code)) {
|
||||
ProxyConfig.authSuccess = true;
|
||||
FileUtil.write("./.neutrino-proxy.license", licenseKey);
|
||||
licenseObtainService.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,3 +18,4 @@ neutrino:
|
||||
server-ip: localhost
|
||||
server-port: 9002
|
||||
ssl-enable: true
|
||||
obtain-license-interval: 5
|
||||
|
||||
@@ -33,7 +33,7 @@ import lombok.Getter;
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum ExceptionEnum {
|
||||
|
||||
SUCCESS(0, "成功"),
|
||||
AUTH_FAILED(1, "认证失败"),
|
||||
CONNECT_FAILED(2, "连接失败");
|
||||
|
||||
|
||||
@@ -106,6 +106,15 @@ public class ProxyMessage {
|
||||
.setInfo(info);
|
||||
}
|
||||
|
||||
public static ProxyMessage buildAuthResultMessage(Integer code, String msg, String licenseKey) {
|
||||
JSONObject data = new JSONObject();
|
||||
data.put("code", code);
|
||||
data.put("msg", msg);
|
||||
data.put("licenseKey", licenseKey);
|
||||
return create().setType(TYPE_AUTH)
|
||||
.setInfo(data.toJSONString());
|
||||
}
|
||||
|
||||
public static ProxyMessage buildConnectMessage(String info) {
|
||||
return create().setType(TYPE_CONNECT)
|
||||
.setInfo(info);
|
||||
|
||||
+19
-18
@@ -85,44 +85,45 @@ public class ProxyMessageAuthHandler implements ProxyMessageHandler {
|
||||
public void handle(ChannelHandlerContext ctx, ProxyMessage proxyMessage) {
|
||||
String licenseKey = proxyMessage.getInfo();
|
||||
if (StringUtil.isEmpty(licenseKey)) {
|
||||
ctx.channel().writeAndFlush(ProxyMessage.buildErrMessage(ExceptionEnum.AUTH_FAILED, "license不能为空!"));
|
||||
ctx.channel().close();
|
||||
ctx.channel().writeAndFlush(ProxyMessage.buildAuthResultMessage(ExceptionEnum.AUTH_FAILED.getCode(), "license不能为空!", licenseKey));
|
||||
// ctx.channel().close();
|
||||
return;
|
||||
}
|
||||
LicenseDO licenseDO = licenseService.findByKey(licenseKey);
|
||||
if (null == licenseDO) {
|
||||
ctx.channel().writeAndFlush(ProxyMessage.buildErrMessage(ExceptionEnum.AUTH_FAILED, "license不存在!"));
|
||||
ctx.channel().close();
|
||||
ctx.channel().writeAndFlush(ProxyMessage.buildAuthResultMessage(ExceptionEnum.AUTH_FAILED.getCode(), "license不存在!", licenseKey));
|
||||
// ctx.channel().close();
|
||||
return;
|
||||
}
|
||||
if (EnableStatusEnum.DISABLE.getStatus().equals(licenseDO.getEnable())) {
|
||||
ctx.channel().writeAndFlush(ProxyMessage.buildErrMessage(ExceptionEnum.AUTH_FAILED, "当前license已被禁用!"));
|
||||
ctx.channel().close();
|
||||
ctx.channel().writeAndFlush(ProxyMessage.buildAuthResultMessage(ExceptionEnum.AUTH_FAILED.getCode(), "当前license已被禁用!", licenseKey));
|
||||
// ctx.channel().close();
|
||||
return;
|
||||
}
|
||||
UserDO userDO = userService.findById(licenseDO.getId());
|
||||
if (null == userDO || EnableStatusEnum.DISABLE.getStatus().equals(userDO.getEnable())) {
|
||||
ctx.channel().writeAndFlush(ProxyMessage.buildErrMessage(ExceptionEnum.AUTH_FAILED, "当前license无效!"));
|
||||
ctx.channel().close();
|
||||
return;
|
||||
}
|
||||
List<PortMappingDO> portMappingList = portMappingService.findEnableListByLicenseId(licenseDO.getId());
|
||||
// 没有端口映射仍然保持连接
|
||||
if (CollectionUtil.isEmpty(portMappingList)) {
|
||||
ctx.channel().writeAndFlush(ProxyMessage.buildAuthResultMessage(ExceptionEnum.AUTH_FAILED.getCode(), "当前license无效!", licenseKey));
|
||||
// ctx.channel().close();
|
||||
return;
|
||||
}
|
||||
Channel cmdChannel = ProxyUtil.getCmdChannelByLicenseId(licenseDO.getId());
|
||||
if (null != cmdChannel) {
|
||||
ctx.channel().writeAndFlush(ProxyMessage.buildErrMessage(ExceptionEnum.AUTH_FAILED, "当前license已被另一节点使用!"));
|
||||
ctx.channel().close();
|
||||
ctx.channel().writeAndFlush(ProxyMessage.buildAuthResultMessage(ExceptionEnum.AUTH_FAILED.getCode(), "当前license已被另一节点使用!", licenseKey));
|
||||
// ctx.channel().close();
|
||||
return;
|
||||
}
|
||||
// 发送认证成功消息
|
||||
ctx.channel().writeAndFlush(ProxyMessage.buildAuthResultMessage(ExceptionEnum.SUCCESS.getCode(), "认证成功!", licenseKey));
|
||||
|
||||
ProxyUtil.initProxyInfo(licenseDO.getId(), ProxyMapping.buildList(portMappingList));
|
||||
List<PortMappingDO> 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()));
|
||||
ProxyUtil.addCmdChannel(licenseDO.getId(), ctx.channel(), portMappingList.stream().map(PortMappingDO::getServerPort).collect(Collectors.toSet()));
|
||||
|
||||
startUserPortServer(ProxyUtil.getAttachInfo(ctx.channel()), portMappingList);
|
||||
startUserPortServer(ProxyUtil.getAttachInfo(ctx.channel()), portMappingList);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user