Java 前后端 WebSocket 完整实现

我给你做一套开箱即用、可直接运行的 Java WebSocket 代码,包含:

后端(SpringBoot + WebSocket)

前端(原生 HTML/JS)

一对一聊天、广播消息、连接状态提示

无任何复杂配置,复制就能跑

一、后端实现(SpringBoot)

  1. 先加 Maven 依赖(pom.xml)
复制代码
> <!-- SpringBoot Web 核心 -->
 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-web</artifactId>
 </dependency>

 <!-- WebSocket 依赖 -->
 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-websocket</artifactId>
 </dependency>
  1. WebSocket 配置类
    开启 WebSocket 支持
java 复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

/**
 * WebSocket 配置类
 */
@Configuration
public class WebSocketConfig {

    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

WebSocket 服务核心类(真正处理连接 / 消息)

java 复制代码
import jakarta.websocket.*;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import org.springframework.stereotype.Component;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;

/**
* WebSocket 服务端
* ws://localhost:8080/ws/用户名
*/
@Component
@ServerEndpoint("/ws/{userId}")
public class WebSocketServer {

   // 静态变量,记录当前在线连接数(线程安全)
   private static int onlineCount = 0;

   // 线程安全 Set,存放每个客户端对应的 WebSocket 对象
   private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<>();

   // 线程安全 Map,存放用户ID与Session对应关系
   private static ConcurrentHashMap<String, Session> sessionMap = new ConcurrentHashMap<>();

   // 与客户端的会话Session,需要通过它给客户端发送数据
   private Session session;
   private String userId;

   /**
    * 连接建立成功调用的方法
    */
   @OnOpen
   public void onOpen(Session session, @PathParam("userId") String userId) {
       this.session = session;
       this.userId = userId;
       sessionMap.put(userId, session);
       webSocketSet.add(this);
       addOnlineCount();

       System.out.println("用户【" + userId + "】连接成功!当前在线人数:" + getOnlineCount());
       sendMessage("连接WebSocket服务端成功!你的ID:" + userId);
   }

   /**
    * 连接关闭调用的方法
    */
   @OnClose
   public void onClose() {
       webSocketSet.remove(this);
       sessionMap.remove(userId);
       subOnlineCount();
       System.out.println("用户【" + userId + "】退出连接!当前在线人数:" + getOnlineCount());
   }

   /**
    * 收到客户端消息后调用的方法
    */
   @OnMessage
   public void onMessage(String message, Session session) {
       System.out.println("收到用户【" + userId + "】消息:" + message);

       // 消息格式:toUserId:内容
       // 例:123:你好
       try {
           if (message.contains(":")) {
               String[] split = message.split(":", 2);
               String toUserId = split[0];
               String content = split[1];
               sendToUser(userId, toUserId, content);
           } else {
               // 无指定用户 → 广播给所有人
               sendToAll("【" + userId + "】说:" + message);
           }
       } catch (Exception e) {
           sendMessage("消息格式错误!正确格式:目标用户ID:消息内容");
       }
   }

   /**
    * 发生错误时调用
    */
   @OnError
   public void onError(Session session, Throwable error) {
       System.out.println("WebSocket 发生错误");
       error.printStackTrace();
   }

   // ==================== 发送消息工具方法 ====================
   /**
    * 发送消息给当前用户
    */
   public void sendMessage(String message) {
       this.session.getAsyncRemote().sendText(message);
   }

   /**
    * 点对点发送消息
    */
   public void sendToUser(String fromUserId, String toUserId, String message) {
       Session session = sessionMap.get(toUserId);
       if (session != null && session.isOpen()) {
           session.getAsyncRemote().sendText("【" + fromUserId + "】私聊你:" + message);
       } else {
           this.sendMessage("用户【" + toUserId + "】不在线!");
       }
   }

   /**
    * 广播消息(所有人)
    */
   public static void sendToAll(String message) {
       for (WebSocketServer webSocket : webSocketSet) {
           webSocket.session.getAsyncRemote().sendText(message);
       }
   }

   // ==================== 在线人数统计 ====================
   public static synchronized int getOnlineCount() {
       return onlineCount;
   }

   public static synchronized void addOnlineCount() {
       WebSocketServer.onlineCount++;
   }

   public static synchronized void subOnlineCount() {
       WebSocketServer.onlineCount--;
   }
}

前端实现(HTML + 原生 JS)

新建一个 ws.html 文件,直接双击打开就能用,无需部署到服务器。

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>WebSocket 聊天</title>
    <style>
        body { max-width: 800px; margin: 20px auto; }
        #msgBox { height: 400px; border: 1px solid #ccc; padding: 10px; overflow-y: auto; margin-bottom: 10px; }
        #message { width: 70%; padding: 8px; }
        button { padding: 8px 16px; cursor: pointer; }
    </style>
</head>
<body>
    <h3>WebSocket 在线聊天</h3>
    <div>
        请输入你的ID:<input type="text" id="userId" placeholder="例如:1001">
        <button onclick="connect()">连接服务端</button>
        <button onclick="closeConnect()">断开连接</button>
    </div>
    <br>
    <div id="msgBox"></div>

    <div>
        <input type="text" id="message" placeholder="发送消息,格式:目标ID:内容(直接输入则广播)">
        <button onclick="sendMsg()">发送</button>
    </div>

<script>
    let ws;
    const msgBox = document.getElementById('msgBox');

    // 连接 WebSocket
    function connect() {
        const userId = document.getElementById('userId').value.trim();
        if (!userId) {
            alert("请输入用户ID!");
            return;
        }

        // 如果已连接,先关闭
        if (ws && ws.readyState !== WebSocket.CLOSED) {
            ws.close();
        }

        // 创建 WebSocket 连接
        ws = new WebSocket("ws://localhost:8080/ws/" + userId);

        // 连接成功
        ws.onopen = function () {
            addMsg("系统", "连接服务端成功!");
        };

        // 收到服务端消息
        ws.onmessage = function (event) {
            addMsg("服务端", event.data);
        };

        // 连接关闭
        ws.onclose = function () {
            addMsg("系统", "已断开连接!");
        };

        // 连接错误
        ws.onerror = function () {
            addMsg("系统", "连接失败!请检查服务端是否启动");
        };
    }

    // 发送消息
    function sendMsg() {
        const msg = document.getElementById('message').value.trim();
        if (!msg || !ws || ws.readyState !== WebSocket.OPEN) {
            alert("未连接或消息为空!");
            return;
        }
        ws.send(msg);
        addMsg("我", msg);
        document.getElementById('message').value = "";
    }

    // 断开连接
    function closeConnect() {
        if (ws) {
            ws.close();
        }
    }

    // 追加消息到页面
    function addMsg(user, text) {
        const div = document.createElement('div');
        div.style.margin = "5px 0";
        div.innerHTML = "<strong>" + user + "</strong>:" + text;
        msgBox.appendChild(div);
        // 滚动到底部
        msgBox.scrollTop = msgBox.scrollHeight;
    }
</script>
</body>
</html>

核心功能说明

  1. 在线人数统计
  2. 点对点私聊
  3. 全局广播
  4. 连接状态实时提示
  5. 线程安全,支持高并发
  6. 前后端完全分离
相关推荐
MY_TEUCK4 小时前
Sealos 平台部署实战指南:结合 Cursor 与版本发布流程
java·人工智能·学习·aigc
2401_873479404 小时前
如何利用IP查询定位识别电商刷单?4个关键指标+工具配置方案
开发语言·tcp/ip·php
我爱cope5 小时前
【从0开始学设计模式-10| 装饰模式】
java·开发语言·设计模式
菜鸟学Python5 小时前
Python生态在悄悄改变:FastAPI全面反超,Django和Flask还行吗?
开发语言·python·django·flask·fastapi
朝新_5 小时前
【Spring AI 】图像与语音模型实战
java·人工智能·spring
RH2312116 小时前
2026.4.16Linux 管道
java·linux·服务器
zmsofts6 小时前
java面试必问13:MyBatis 一级缓存、二级缓存:从原理到脏数据,一篇讲透
java·面试·mybatis
浪浪小洋6 小时前
c++ qt课设定制
开发语言·c++
charlie1145141916 小时前
嵌入式C++工程实践第16篇:第四次重构 —— LED模板,从通用GPIO到专用抽象
c语言·开发语言·c++·驱动开发·嵌入式硬件·重构
故事和你916 小时前
洛谷-数据结构1-4-图的基本应用1
开发语言·数据结构·算法·深度优先·动态规划·图论