From c1bb3dd69c4d68f3f0787b59881ca258707b8cea Mon Sep 17 00:00:00 2001
From: Administrator <15274802129@163.com>
Date: Thu, 14 Aug 2025 10:34:36 +0800
Subject: [PATCH] feat(websocket): 添加 Netty WebSocket 聊天功能基础版本,URL:http://localhost:8085/febs/pages/websocket/chat.html
---
src/main/java/cc/mrbird/febs/common/configure/NettyWebSocketConfig.java | 54 ++++++++++
src/main/resources/static/febs/pages/websocket/chat.html | 134 ++++++++++++++++++++++++++
src/main/java/cc/mrbird/febs/common/websocket/WebSocketServerHandler.java | 48 +++++++++
pom.xml | 8 +
src/main/java/cc/mrbird/febs/common/websocket/WebSocketServer.java | 45 +++++++++
src/main/resources/application.yml | 4
6 files changed, 293 insertions(+), 0 deletions(-)
diff --git a/pom.xml b/pom.xml
index 56ecf7c..9fa5ea9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -29,6 +29,14 @@
<dependencies>
+
+
+ <dependency>
+ <groupId>io.netty</groupId>
+ <artifactId>netty-all</artifactId>
+ <version>4.1.92.Final</version> <!-- 示例版本 -->
+ </dependency>
+
<dependency>
<groupId>com.volcengine</groupId>
<artifactId>volcengine-java-sdk-ark-runtime</artifactId>
diff --git a/src/main/java/cc/mrbird/febs/common/configure/NettyWebSocketConfig.java b/src/main/java/cc/mrbird/febs/common/configure/NettyWebSocketConfig.java
new file mode 100644
index 0000000..7065701
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/common/configure/NettyWebSocketConfig.java
@@ -0,0 +1,54 @@
+package cc.mrbird.febs.common.configure;
+
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import io.netty.handler.codec.http.HttpObjectAggregator;
+import io.netty.handler.codec.http.HttpServerCodec;
+import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
+import io.netty.handler.logging.LogLevel;
+import io.netty.handler.logging.LoggingHandler;
+import io.netty.handler.stream.ChunkedWriteHandler;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class NettyWebSocketConfig {
+
+ @Value("${netty.websocket.port:8090}")
+ private int port;
+
+ @Bean(name = "bossGroup", destroyMethod = "shutdownGracefully")
+ public EventLoopGroup bossGroup() {
+ return new NioEventLoopGroup(1);
+ }
+
+ @Bean(name = "workerGroup", destroyMethod = "shutdownGracefully")
+ public EventLoopGroup workerGroup() {
+ return new NioEventLoopGroup();
+ }
+
+ @Bean
+ public ServerBootstrap serverBootstrap(EventLoopGroup bossGroup, EventLoopGroup workerGroup) {
+ ServerBootstrap bootstrap = new ServerBootstrap();
+ bootstrap.group(bossGroup, workerGroup)
+ .channel(NioServerSocketChannel.class)
+ .option(ChannelOption.SO_BACKLOG, 128)
+ .childOption(ChannelOption.SO_KEEPALIVE, true)
+ .handler(new LoggingHandler(LogLevel.INFO))
+ .childHandler(new io.netty.channel.ChannelInitializer<io.netty.channel.socket.SocketChannel>() {
+ @Override
+ protected void initChannel(io.netty.channel.socket.SocketChannel ch) throws Exception {
+ ch.pipeline().addLast(new HttpServerCodec());
+ ch.pipeline().addLast(new ChunkedWriteHandler());
+ ch.pipeline().addLast(new HttpObjectAggregator(65536));
+ ch.pipeline().addLast(new WebSocketServerProtocolHandler("/ws"));
+ ch.pipeline().addLast(new cc.mrbird.febs.common.websocket.WebSocketServerHandler());
+ }
+ });
+ return bootstrap;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/cc/mrbird/febs/common/websocket/WebSocketServer.java b/src/main/java/cc/mrbird/febs/common/websocket/WebSocketServer.java
new file mode 100644
index 0000000..9fdc62a
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/common/websocket/WebSocketServer.java
@@ -0,0 +1,45 @@
+package cc.mrbird.febs.common.websocket;
+
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.EventLoopGroup;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+
+@Component
+public class WebSocketServer {
+
+ @Value("${netty.websocket.port:8090}")
+ private int port;
+
+ @Autowired
+ private ServerBootstrap serverBootstrap;
+
+ @Autowired
+ private EventLoopGroup bossGroup;
+
+ @Autowired
+ private EventLoopGroup workerGroup;
+
+ private ChannelFuture channelFuture;
+
+ @PostConstruct
+ public void start() throws InterruptedException {
+ channelFuture = serverBootstrap.bind(port).sync();
+ System.out.println("Netty WebSocket服务器启动在端口:" + port);
+ }
+
+ @PreDestroy
+ public void stop() {
+ if (channelFuture != null) {
+ channelFuture.channel().close();
+ }
+ bossGroup.shutdownGracefully();
+ workerGroup.shutdownGracefully();
+ System.out.println("Netty WebSocket服务器已停止");
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/cc/mrbird/febs/common/websocket/WebSocketServerHandler.java b/src/main/java/cc/mrbird/febs/common/websocket/WebSocketServerHandler.java
new file mode 100644
index 0000000..3506dd4
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/common/websocket/WebSocketServerHandler.java
@@ -0,0 +1,48 @@
+package cc.mrbird.febs.common.websocket;
+
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+import io.netty.channel.group.ChannelGroup;
+import io.netty.channel.group.DefaultChannelGroup;
+import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
+import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
+import io.netty.util.concurrent.GlobalEventExecutor;
+
+public class WebSocketServerHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
+
+ public static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
+
+ @Override
+ public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
+ channels.add(ctx.channel());
+ }
+
+ @Override
+ public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
+ channels.remove(ctx.channel());
+ }
+
+ @Override
+ public void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
+ String request = msg.text();
+ System.out.println("服务端收到:" + request);
+
+ // 广播消息给其他连接的客户端,不包括发送者自己
+ for (io.netty.channel.Channel channel : channels) {
+ if (channel != ctx.channel()) {
+ channel.writeAndFlush(new TextWebSocketFrame("[用户] " + request));
+ }
+ }
+ }
+
+ @Override
+ public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
+ ctx.flush();
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+ cause.printStackTrace();
+ ctx.close();
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 8cff713..608f7d0 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -41,3 +41,7 @@
metadata:
TableInfoHelper: error
cc.mrbird.febs: info
+
+netty:
+ websocket:
+ port: 8090
diff --git a/src/main/resources/static/febs/pages/websocket/chat.html b/src/main/resources/static/febs/pages/websocket/chat.html
new file mode 100644
index 0000000..148ad8c
--- /dev/null
+++ b/src/main/resources/static/febs/pages/websocket/chat.html
@@ -0,0 +1,134 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+ <meta charset="UTF-8">
+ <title>WebSocket聊天室</title>
+ <link rel="stylesheet" href="/layui/css/layui.css" media="all">
+ <style>
+ .chat-container {
+ width: 100%;
+ max-width: 800px;
+ margin: 20px auto;
+ border: 1px solid #e6e6e6;
+ border-radius: 4px;
+ overflow: hidden;
+ }
+ .chat-messages {
+ height: 400px;
+ padding: 15px;
+ overflow-y: auto;
+ background-color: #f8f8f8;
+ }
+ .chat-input {
+ padding: 15px;
+ background-color: #fff;
+ }
+ .message {
+ margin-bottom: 10px;
+ padding: 8px 12px;
+ border-radius: 4px;
+ background-color: #fff;
+ box-shadow: 0 1px 2px rgba(0,0,0,0.1);
+ }
+ .message.me {
+ background-color: #1E9FFF;
+ color: white;
+ text-align: right;
+ }
+ </style>
+</head>
+<body>
+<div class="layui-container">
+ <div class="chat-container">
+ <div class="chat-messages" id="chatMessages">
+ <!-- 消息将在这里显示 -->
+ </div>
+ <div class="chat-input">
+ <div class="layui-form">
+ <div class="layui-form-item">
+ <div class="layui-input-block">
+ <input type="text" id="messageInput" placeholder="请输入消息..." class="layui-input">
+ </div>
+ </div>
+ <div class="layui-form-item">
+ <div class="layui-input-block">
+ <button class="layui-btn" id="sendBtn">发送消息</button>
+ <button class="layui-btn layui-btn-primary" id="connectBtn">连接</button>
+ <button class="layui-btn layui-btn-danger" id="disconnectBtn">断开</button>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<script src="/layui/layui.js" charset="utf-8"></script>
+<script>
+ layui.use(['jquery'], function() {
+ var $ = layui.jquery;
+ var websocket;
+
+ function connect() {
+ if ('WebSocket' in window) {
+ // 连接到Netty WebSocket服务器
+ websocket = new WebSocket("ws://localhost:8090/ws");
+
+ websocket.onopen = function() {
+ appendMessage("[系统] 连接成功", "system");
+ };
+
+ websocket.onmessage = function(event) {
+ appendMessage(event.data, "other");
+ };
+
+ websocket.onclose = function() {
+ appendMessage("[系统] 连接已关闭", "system");
+ };
+
+ websocket.onerror = function() {
+ appendMessage("[系统] 连接发生错误", "system");
+ };
+ } else {
+ alert('您的浏览器不支持WebSocket');
+ }
+ }
+
+ function disconnect() {
+ if (websocket) {
+ websocket.close();
+ }
+ }
+
+ function sendMessage() {
+ var message = $('#messageInput').val().trim();
+ if (message && websocket) {
+ websocket.send(message);
+ appendMessage(message, "me");
+ $('#messageInput').val('');
+ }
+ }
+
+ function appendMessage(message, type) {
+ var messageClass = "message";
+ if (type === "me") {
+ messageClass += " me";
+ }
+ $('#chatMessages').append('<div class="' + messageClass + '">' + message + '</div>');
+ $('#chatMessages').scrollTop($('#chatMessages')[0].scrollHeight);
+ }
+
+ // 绑定事件
+ $('#connectBtn').on('click', connect);
+ $('#disconnectBtn').on('click', disconnect);
+ $('#sendBtn').on('click', sendMessage);
+
+ // 回车发送消息
+ $('#messageInput').on('keypress', function(e) {
+ if (e.which === 13) {
+ sendMessage();
+ }
+ });
+ });
+</script>
+</body>
+</html>
\ No newline at end of file
--
Gitblit v1.9.1