跳到主要内容
极客日志极客日志
首页博客AI提示词GitHub精选代理工具
|注册
博客列表

目录

  1. 说明
  2. 一、功能介绍
  3. 功能特性
  4. 项目结构
  5. 关键代码位置
  6. 测试连接
  7. 二、运行测试
  8. 三、核心代码说明
  9. ChatWebSocketHandler.java
  10. WebSocketConfig.java
Javajava

基于 Spring Boot 的 WebSocket 服务示例

本文介绍了一个基于 Java JDK 8 和 Spring Boot 实现的 WebSocket 演示项目。项目旨在解决多端消息通讯问题,支持用户隔离、分片设置及缓冲区大小调整。核心代码包含 WebSocket 配置类与文本/二进制消息处理器,实现了全双工通信、P2P 消息路由及会话管理。通过静态测试页面可验证连接与消息收发功能。

奶糖兔发布于 2026/3/29更新于 2026/4/131 浏览
基于 Spring Boot 的 WebSocket 服务示例

说明

这是一个使用 Java JDK 8 和 Spring Boot 实现的 WebSocket 演示项目。目的是为解决多端消息通讯的问题。

WebSocket 是一种基于 TCP 的全双工通信协议,核心作用是解决传统 HTTP 协议'请求 - 响应'模式的局限性,实现客户端与服务器之间的实时、双向、低延迟数据传输。

一、功能介绍

功能特性

  • 基于 Maven 的 Spring Boot 项目骨架。
  • 纯 WebSocket 端点 /ws,支持用户隔离,http:使用 ws,https:使用 wss。
  • 支持分片设置和缓冲区大小设置,解决传输内容限制。
  • 提供静态测试页面 index.html,用于连接、发送消息、查看消息。

项目结构

  • pom.xml:Spring Boot 3.3,依赖 spring-boot-starter-web 和 spring-boot-starter-websocket。
  • src/main/java/com/example/websocket/WebSocketApplication.java:应用入口。
  • src/main/java/com/example/websocket/WebSocketConfig.java:注册 WebSocket 处理器,端点为 /ws。
  • src/main/java/com/example/websocket/ChatWebSocketHandler.java:文本消息处理,广播到所有会话。
  • src/main/resources/static/index.html:页面内置 JS,连接 ws://{host}/ws,可发送、显示消息。

关键代码位置

  • 启动类:src/main/java/com/example/websocket/WebSocketApplication.java
  • WebSocket 配置:src/main/java/com/example/websocket/WebSocketConfig.java
  • 文本消息处理器:src/main/java/com/example/websocket/ChatWebSocketHandler.java
  • 静态页面:src/main/resources/static/index.html

测试连接

  • 打开 http://localhost:8800,使用页面上的'连接/发送'测试。
  • WebSocket 地址:ws://localhost:8080/ws

二、运行测试

可通过 UserId 来创建独立的联接,进行用户隔离。

三、核心代码说明

由于 websocket 对传输的内容有限制,若内容较大可进行缓冲区大小设置,并对不同文本进行分片处理。

ChatWebSocketHandler.java

package com.example.websocket;

import java.io.ByteArrayOutputStream;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.springframework.web.socket.BinaryMessage;
 org.springframework.web.socket.CloseStatus;
 org.springframework.web.socket.TextMessage;
 org.springframework.web.socket.WebSocketSession;
 org.springframework.web.socket.handler.AbstractWebSocketHandler;

 com.fasterxml.jackson.databind.ObjectMapper;

     {
      ConcurrentHashMap<String, Set<WebSocketSession>> userSessions =  <>();
           ();
      ConcurrentHashMap<String, StringBuilder> textFragments =  <>();
      ConcurrentHashMap<String, ByteArrayOutputStream> binaryFragments =  <>();

    
        Exception {
        
           resolveUserId(session);
         (uid ==  || uid.isEmpty()) {
            session.close(CloseStatus.BAD_DATA);
            ;
        }
        session.getAttributes().put(, uid);
        
        userSessions.computeIfAbsent(uid, k -> ConcurrentHashMap.newKeySet()).add(session);
    }

    
        Exception {
        
           session.getId();
         (!message.isLast()) {
            textFragments.computeIfAbsent(id, k ->  ()).append(message.getPayload());
            ;
        }
           textFragments.remove(id);
           sb !=  ? sb.append(message.getPayload()).toString() : message.getPayload();
        routePayload(session, payload);
    }

    
        Exception {
        
           session.getId();
           message.getPayload();
        [] chunk =  [buf.remaining()];
        buf.get(chunk);
           binaryFragments.computeIfAbsent(id, k ->  ());
        acc.write(chunk);
         (message.isLast()) {
            [] all = acc.toByteArray();
            binaryFragments.remove(id);
                (all, StandardCharsets.UTF_8);
            routePayload(session, payload);
        }
    }

    
       {
         ;
    }

    
        Exception {
        
           session.getAttributes().get();
         (v == ) ;
           String.valueOf(v);
        Set<WebSocketSession> set = userSessions.get(uid);
         (set != ) {
            set.remove(session);
             (set.isEmpty()) userSessions.remove(uid);
        }
    }

    
     String  {
           session.getUri();
         (uri == )  ;
           uri.getQuery();
         (q ==  || q.isEmpty())  ;
        String[] parts = q.split();
         (String p : parts) {
               p.indexOf();
             (i > ) {
                   p.substring(, i);
                   p.substring(i + );
                 (.equals(k))  val;
            }
        }
         ;
    }

        Exception {
           session.getAttributes().get();
         (v == ) ;
           String.valueOf(v);
        
            ();
        message.setFromUserId(fromUid);
         {
            
               MAPPER.readValue(payload, Message.class);
            message.setToUserId(receivedMsg.getToUserId());
            message.setContent(receivedMsg.getContent());
            message.setType(receivedMsg.getType());
        }  (Exception e) {
            
            message.setContent(payload);
        }
           message.getToUserId();
           toUid !=  && !toUid.isEmpty();
        Set<WebSocketSession> targets;
         (isP2P) {
            targets = userSessions.get(toUid);
        }  {
            targets = userSessions.get(fromUid);
        }
        
           MAPPER.writeValueAsString(message);
            (outStr);
         (targets ==  || targets.isEmpty()) {
             (session.isOpen()) {
                session.sendMessage(msg);
            }
            ;
        }
         (WebSocketSession s : targets) {
             (s.isOpen()) {
                s.sendMessage(msg);
            }
        }
         (isP2P && session.isOpen()) {
            session.sendMessage(msg);
        }
    }
}
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog

更多推荐文章

查看全部
  • MIT 电机混合扭矩模式控制详解
  • 绿联云 NAS 配置 WebDAV 实现公网文献同步
  • 基于 MCP Server 与 Figma AI Bridge 自动生成前端代码教程
  • PX4 无人机结合 MID360 与 FAST-LIO 实现室内定位及定点
  • Awesome GitHub Copilot 自定义功能介绍
  • PRIDE-PPPAR:多系统 GNSS 精密定位开源解决方案
  • Stable Diffusion AKI V4 整合包本地部署指南
  • Claude Code 辅助 Verilog 编程与 FPGA 设计实战

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

import
import
import
import
import
public
class
ChatWebSocketHandler
extends
AbstractWebSocketHandler
private
final
new
ConcurrentHashMap
private
static
final
ObjectMapper
MAPPER
=
new
ObjectMapper
private
final
new
ConcurrentHashMap
private
final
new
ConcurrentHashMap
@Override
public
void
afterConnectionEstablished
(WebSocketSession session)
throws
// 验证用户 ID 的有效性
String
uid
=
if
null
return
"userId"
// 多会话管理
@Override
protected
void
handleTextMessage
(WebSocketSession session, TextMessage message)
throws
// 分片处理
String
id
=
if
new
StringBuilder
return
StringBuilder
sb
=
String
payload
=
null
@Override
protected
void
handleBinaryMessage
(WebSocketSession session, BinaryMessage message)
throws
// 二进制消息处理
String
id
=
ByteBuffer
buf
=
byte
new
byte
ByteArrayOutputStream
acc
=
new
ByteArrayOutputStream
if
byte
String
payload
=
new
String
@Override
public
boolean
supportsPartialMessages
()
return
true
@Override
public
void
afterConnectionClosed
(WebSocketSession session, CloseStatus status)
throws
// WebSocket 连接关闭时的清理逻辑
Object
v
=
"userId"
if
null
return
String
uid
=
if
null
if
/** 从 WebSocket 连接的 URL 查询参数中提取用户 ID */
private
resolveUserId
(WebSocketSession session)
URI
uri
=
if
null
return
null
String
q
=
if
null
return
null
"&"
for
int
i
=
'='
if
0
String
k
=
0
String
val
=
1
if
"userId"
return
return
null
private
void
routePayload
(WebSocketSession session, String payload)
throws
Object
v
=
"userId"
if
null
return
String
fromUid
=
// 解析消息
Message
message
=
new
Message
try
// 尝试将 payload 解析为 Message 对象
Message
receivedMsg
=
catch
// 如果解析失败,将整个 payload 作为 content
String
toUid
=
boolean
isP2P
=
null
if
else
// 序列化消息对象
String
outStr
=
TextMessage
msg
=
new
TextMessage
if
null
if
return
for
if
if

WebSocketConfig.java

package com.example.websocket;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean;

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(chatHandler(), "/ws").setAllowedOriginPatterns("*");
    }

    @Bean
    public WebSocketHandler chatHandler() {
        return new ChatWebSocketHandler();
    }

    // 配置 WebSocket 容器参数(解决消息过大、超时等问题)
    @Bean
    public ServletServerContainerFactoryBean createWebSocketContainer() {
        ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
        // 文本消息缓冲区:2MB(解决解码后消息过大的核心配置)
        container.setMaxTextMessageBufferSize(2 * 1024 * 1024);
        // 二进制消息缓冲区:4MB(按需配置)
        container.setMaxBinaryMessageBufferSize(4 * 1024 * 1024);
        // 会话空闲超时:60 秒(无交互则关闭连接)
        container.setMaxSessionIdleTimeout(60_000L);
        return container;
    }
}
算法入门:时间复杂度与基础排序算法
  • 《AI 提效手册》深度解读:五款主流 AI 工具实战指南
  • Dify 搭建发票识别助手实战指南
  • Whisper 语音识别快速入门:从安装到使用
  • 2026 年主流 AI 生成 PPT 工具评测与推荐
  • 无人机 5.8G 模拟图传电路设计方案及性能分析
  • 前端请求后端返回 404/405/500 状态码:排查与解决指南
  • 第十五届蓝桥杯 Python B 组省赛真题解析
  • 服务器或本地部署鸣潮 QQ 机器人,实现签到与练度查询功能
  • LG WebOS 电视 Homebrew Channel 第三方应用安装与配置指南
  • PyCharm 中 Copilot 插件 Claude 模型不可用解决方案
  • FASTLIVO2 算法解析与实战(一):系统架构与核心模块详解