From edeeaef30868086cc31a4498223b04f48721d3d4 Mon Sep 17 00:00:00 2001 From: Frish2021 <1573880184@qq.com> Date: Sat, 20 Sep 2025 00:49:41 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=BA=86banner.txt=E8=87=AA?= =?UTF-8?q?=E5=AE=9A=E4=B9=89banner=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../socket/transport/AioQuickServer.java | 27 +++++-- .../smartboot/socket/transport/Banner.java | 32 ++++++++ .../socket/transport/BannerFormat.java | 81 +++++++++++++++++++ .../smartboot/socket/transport/IOUtil.java | 20 +++++ .../socket/transport/IoServerConfig.java | 2 + .../socket/transport/UdpBootstrap.java | 19 ++++- .../socket/example/push/PushServer.java | 1 + example/src/main/resources/banner.txt | 5 ++ 8 files changed, 177 insertions(+), 10 deletions(-) create mode 100644 aio-core/src/main/java/org/smartboot/socket/transport/Banner.java create mode 100644 aio-core/src/main/java/org/smartboot/socket/transport/BannerFormat.java create mode 100644 example/src/main/resources/banner.txt diff --git a/aio-core/src/main/java/org/smartboot/socket/transport/AioQuickServer.java b/aio-core/src/main/java/org/smartboot/socket/transport/AioQuickServer.java index eabf49574..8a24f42b1 100644 --- a/aio-core/src/main/java/org/smartboot/socket/transport/AioQuickServer.java +++ b/aio-core/src/main/java/org/smartboot/socket/transport/AioQuickServer.java @@ -26,6 +26,7 @@ import java.nio.channels.CompletionHandler; import java.security.InvalidParameterException; import java.util.Map; +import java.util.function.Consumer; import java.util.function.Supplier; /** @@ -66,6 +67,12 @@ public final class AioQuickServer { *

调用AioQuickClient的各setXX()方法,都是为了设置config的各配置项

*/ private final IoServerConfig config = new IoServerConfig(); + + /** + * banner实例 + *

可以通过资源文件里面的banner.txt来自定义banner

+ */ + private final Banner banner; private static long threadSeqNumber; /** * write 内存池 @@ -76,6 +83,16 @@ public final class AioQuickServer { */ private BufferPagePool readBufferPool = null; + /** + * 默认banner + */ + private final Consumer defaultBanner = (config) -> { + System.out.println(IoServerConfig.BANNER + "\r\n :: smart-socket " + "::\t(" + IoServerConfig.VERSION + ") [port: " + config.getPort() + ", threadNum:" + config.getThreadNum() + "]"); + System.out.println("Technical Support:"); + System.out.println(" - Document: https://smartboot.tech]"); + System.out.println(" - Gitee: https://gitee.com/smartboot/smart-socket"); + System.out.println(" - Github: https://github.com/smartboot/smart-socket"); + }; /** * 设置服务端启动必要参数配置 @@ -89,6 +106,8 @@ public AioQuickServer(int port, Protocol protocol, MessageProcessor me config.setProtocol(protocol); config.setProcessor(messageProcessor); config.setThreadNum(Runtime.getRuntime().availableProcessors()); + + this.banner = new Banner(config); } /** @@ -118,13 +137,7 @@ public void start() throws IOException { * @throws IOException IO异常 */ public void start(AsynchronousChannelGroup asynchronousChannelGroup) throws IOException { - if (config.isBannerEnabled()) { - System.out.println(IoServerConfig.BANNER + "\r\n :: smart-socket " + "::\t(" + IoServerConfig.VERSION + ") [port: " + config.getPort() + ", threadNum:" + config.getThreadNum() + "]"); - System.out.println("Technical Support:"); - System.out.println(" - Document: https://smartboot.tech]"); - System.out.println(" - Gitee: https://gitee.com/smartboot/smart-socket"); - System.out.println(" - Github: https://github.com/smartboot/smart-socket"); - } + if (config.isBannerEnabled()) banner.printBanner("banner.txt", defaultBanner); try { if (writeBufferPool == null) { this.writeBufferPool = BufferPagePool.DEFAULT_BUFFER_PAGE_POOL; diff --git a/aio-core/src/main/java/org/smartboot/socket/transport/Banner.java b/aio-core/src/main/java/org/smartboot/socket/transport/Banner.java new file mode 100644 index 000000000..ad3348159 --- /dev/null +++ b/aio-core/src/main/java/org/smartboot/socket/transport/Banner.java @@ -0,0 +1,32 @@ +package org.smartboot.socket.transport; + +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import java.util.function.Consumer; + +/** + * @author Frish2021 + * 用于输出banner + */ +final class Banner { + private final IoServerConfig config; + private final BannerFormat format; + + Banner(IoServerConfig config) { + this.config = config; + this.format = new BannerFormat(config); + } + + void printBanner(String fileName, Consumer defaultPrint) throws IOException { + try(InputStream in = AioQuickServer.class.getClassLoader().getResourceAsStream(fileName)) { + if (in == null) { + defaultPrint.accept(config); + return; + } + + List lines = IOUtil.readLineFromStream(in); + lines.forEach(format::printf); + } + } +} diff --git a/aio-core/src/main/java/org/smartboot/socket/transport/BannerFormat.java b/aio-core/src/main/java/org/smartboot/socket/transport/BannerFormat.java new file mode 100644 index 000000000..fda78bdbe --- /dev/null +++ b/aio-core/src/main/java/org/smartboot/socket/transport/BannerFormat.java @@ -0,0 +1,81 @@ +package org.smartboot.socket.transport; + +import java.util.Map; +import java.util.WeakHashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @author Frish2021 + * 用于格式化banner以及提供格式化banner时的占位符API + */ +final class BannerFormat { + private final static Pattern pattern = Pattern.compile("\\$\\{([^}]+)}"); + private final Map map = new WeakHashMap<>(); + private final IoServerConfig config; + + BannerFormat(IoServerConfig config) { + this.config = config; + + { + map.put("version", ignore -> IoServerConfig.VERSION); + map.put("port", IoServerConfig::getPort); + map.put("threads", IoServerConfig::getThreadNum); + map.put("backlog", IoServerConfig::getBacklog); + } + } + + void printf(String line) { + Matcher matcher = pattern.matcher(line); + StringBuffer result = new StringBuffer(); + replaceAll(matcher, (key) -> containsKeyReplace(key, (replacement) -> { + matcher.appendReplacement(result, replacement); + })); + + matcher.appendTail(result); + System.out.println(result); + } + + /** + * 替换展位符的同时判断是否是存在的占位符 + */ + private void containsKeyReplace(String key, Replace replace) { + if (map.containsKey(key)) { + String replacement = toString(key); + replace.replace(replacement); + } + } + + /** + * 替换所有的占位符 + */ + private void replaceAll(Matcher matcher, Replace replace) { + while (matcher.find()) { + String key = matcher.group(1); + replace.replace(key); + } + } + + /** + * 获取到展位符的值 + */ + private String toString(String key) { + return map.get(key).format(config).toString(); + } + + /** + * 用于replaceAll和containsKeyReplace方法的封装 + */ + @FunctionalInterface + interface Replace { + void replace(String key); + } + + /** + * 占位符格式化接口 + */ + @FunctionalInterface + interface Format { + Object format(IoServerConfig config); + } +} diff --git a/aio-core/src/main/java/org/smartboot/socket/transport/IOUtil.java b/aio-core/src/main/java/org/smartboot/socket/transport/IOUtil.java index e5455987a..2f233f810 100644 --- a/aio-core/src/main/java/org/smartboot/socket/transport/IOUtil.java +++ b/aio-core/src/main/java/org/smartboot/socket/transport/IOUtil.java @@ -9,9 +9,15 @@ package org.smartboot.socket.transport; +import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.nio.channels.AsynchronousSocketChannel; import java.nio.channels.NotYetConnectedException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; /** * @author 三刀 @@ -41,4 +47,18 @@ static void close(AsynchronousSocketChannel channel) { } } + /** + * @param in 文件输入流 + */ + static List readLineFromStream(InputStream in) throws IOException { + List result = new ArrayList<>(); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))) { + String line; + while ((line = reader.readLine()) != null) { + result.add(line); + } + } + + return result; + } } diff --git a/aio-core/src/main/java/org/smartboot/socket/transport/IoServerConfig.java b/aio-core/src/main/java/org/smartboot/socket/transport/IoServerConfig.java index a53a4000a..49f498bad 100644 --- a/aio-core/src/main/java/org/smartboot/socket/transport/IoServerConfig.java +++ b/aio-core/src/main/java/org/smartboot/socket/transport/IoServerConfig.java @@ -27,7 +27,9 @@ final class IoServerConfig { /** * banner信息 + * @deprecated 通过banner.txt可以自定义banner内容 */ + @Deprecated public static final String BANNER = "\n" + " _ _ _ \n" + " ( )_ ( ) ( )_ \n" + diff --git a/aio-pro/src/main/java/org/smartboot/socket/transport/UdpBootstrap.java b/aio-pro/src/main/java/org/smartboot/socket/transport/UdpBootstrap.java index 35db5e19b..3f7094215 100644 --- a/aio-pro/src/main/java/org/smartboot/socket/transport/UdpBootstrap.java +++ b/aio-pro/src/main/java/org/smartboot/socket/transport/UdpBootstrap.java @@ -16,6 +16,7 @@ import java.io.IOException; import java.net.InetSocketAddress; import java.nio.channels.DatagramChannel; +import java.util.function.Consumer; /** * UDP服务启动类 @@ -41,6 +42,18 @@ public class UdpBootstrap { private Worker worker; private boolean innerWorker = false; + /** + * banner实例 + *

可以通过banner.txt来自定义

+ */ + private final Banner banner; + + /** + * 默认banner + */ + private final Consumer defaultBanner = (config) -> { + System.out.println(IoServerConfig.BANNER + "\r\n :: smart-socket[udp] ::\t(" + IoServerConfig.VERSION + ")"); + }; public UdpBootstrap(Protocol protocol, MessageProcessor messageProcessor, Worker worker) { this(protocol, messageProcessor); @@ -50,6 +63,8 @@ public UdpBootstrap(Protocol protocol, MessageProcessor UdpBootstrap(Protocol protocol, MessageProcessor messageProcessor) { config.setProtocol(protocol); config.setProcessor(messageProcessor); + + this.banner = new Banner(config); } /** @@ -78,9 +93,7 @@ public UdpChannel open(int port) throws IOException { */ public UdpChannel open(String host, int port) throws IOException { // 增加广告说明 - if (config.isBannerEnabled()) { - System.out.println(IoServerConfig.BANNER + "\r\n :: smart-socket[udp] ::\t(" + IoServerConfig.VERSION + ")"); - } + if (config.isBannerEnabled()) banner.printBanner("banner.txt", defaultBanner); //初始化内存池 if (writeBufferPool == null) { this.writeBufferPool = BufferPagePool.DEFAULT_BUFFER_PAGE_POOL; diff --git a/example/src/main/java/org/smartboot/socket/example/push/PushServer.java b/example/src/main/java/org/smartboot/socket/example/push/PushServer.java index 15297783e..624061411 100644 --- a/example/src/main/java/org/smartboot/socket/example/push/PushServer.java +++ b/example/src/main/java/org/smartboot/socket/example/push/PushServer.java @@ -21,6 +21,7 @@ public class PushServer { public static void main(String[] args) throws IOException { AioQuickServer server = new AioQuickServer(8080, new StringProtocol(), new PushServerProcessorMessage()); + server.setBannerEnabled(true); server.start(); } } diff --git a/example/src/main/resources/banner.txt b/example/src/main/resources/banner.txt new file mode 100644 index 000000000..a456acd8d --- /dev/null +++ b/example/src/main/resources/banner.txt @@ -0,0 +1,5 @@ +Version: ${version} | Port: ${port} | backlog: ${backlog} +Technical Support: + - Document: https://smartboot.tech] + - Gitee: https://gitee.com/smartboot/smart-socket + - Github: https://github.com/smartboot/smart-socket