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. 前后端完全分离
相关推荐
不喝水的鱼儿2 小时前
KT Qwen3.5-35B-A3B 记录
java·前端·python
小此方2 小时前
Re:从零开始的 C++ STL篇(九)AVL树太“较真”,红黑树更“现实”:一文讲透工程中的平衡之道
开发语言·数据结构·c++·算法·stl
少许极端2 小时前
算法奇妙屋(三十八)-贪心算法学习之路 5
java·学习·算法·贪心算法
woai33642 小时前
学习JVM-基础篇-Java虚拟机栈&本地方法栈
java·jvm·学习
小陈工2 小时前
Python Web开发入门(三):配置文件管理与环境变量最佳实践
开发语言·jvm·数据库·python·oracle·性能优化·开源
ybwycx2 小时前
springboot3整合knife4j详细版,包会!(不带swagger2玩)
java
极客先躯2 小时前
高级java每日一道面试题-2025年9月23日-企业集成篇[LangChain4j]-如何与现有的企业中间件集成(Kafka、RabbitMQ)?
java·中间件·java-rabbitmq·稳定性·可靠性·扩展性·langchain4j
cch89182 小时前
PHP vs Java:主流编程语言深度对比
java·开发语言·php
曹牧2 小时前
Tomcat中间件能够提供的能力
java·中间件·tomcat