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. 前后端完全分离
相关推荐
Finger#0000FF3 分钟前
从零上手VibeCoding(ClaudeCode+DeepSeek V4.Pro)
java·人工智能·ai编程·vibe coding·claudecode
木子墨5163 分钟前
系统设计面试 | 实现一个限流器:滑动窗口 → 令牌桶 → 漏桶
java·开发语言·数据结构·数据库·面试·职场和发展
吴声子夜歌15 分钟前
Java——synchronized
java·synchronized
不知名的忻22 分钟前
交换排序:冒泡排序 vs 快速排序(Java)
java·算法·排序算法
程序员阿明22 分钟前
spring boot + vue3 实现RSA加密解密
java·spring boot·后端
Byte Wizard30 分钟前
C语言指针深入浅出6
c语言·开发语言
知识分享小能手35 分钟前
R语言入门学习教程,从入门到精通,R语言数据处理与清洗 (9)
开发语言·学习·r语言
wok15739 分钟前
IDEA 无法识别 OkHttpClient?cannot resolve symbol问题解决
java·ide·intellij-idea
吴声子夜歌44 分钟前
Java——标准序列化机制
java·序列化
hughnz1 小时前
下一代地热能的技术障碍
java·大数据·数据库