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