当前位置: 代码网 > it编程>编程语言>Java > WebSocket详解及Spring Boot+Java高级实战用法示例

WebSocket详解及Spring Boot+Java高级实战用法示例

2026年09月02日 Java 我要评论
一、为什么需要 websocket?传统的 web 应用基于http 协议,是请求-响应模式:客户端发请求 → 服务端处理 → 返回结果 → 连接关闭如果服务端有新数据(

一、为什么需要 websocket?

传统的 web 应用基于 http 协议,是请求-响应模式:

  • 客户端发请求 → 服务端处理 → 返回结果 → 连接关闭
  • 如果服务端有新数据(如聊天消息、订单状态变更),无法主动推给客户端

为实现“实时通信”,早期常用 轮询(polling) 或 长轮询(long polling)

  • 轮询:每隔几秒发一次请求 → 浪费带宽、延迟高
  • 长轮询:请求挂起直到有数据 → 仍基于 http,连接频繁建立/断开

✅ websocket 的优势

  • 全双工通信:客户端和服务端可随时互相发送数据
  • 单 tcp 连接:握手后复用连接,低开销
  • 低延迟、高效率:适合实时场景(聊天、股票、游戏、监控)

二、websocket 协议简析

  • 握手阶段:客户端通过 http 发起 upgrade 请求

    get /chat http/1.1
    host: server.example.com
    upgrade: websocket
    connection: upgrade
    sec-websocket-key: dghlihnhbxbszsbub25jzq==
    sec-websocket-version: 13
    
  • 服务端响应

    http/1.1 101 switching protocols
    upgrade: websocket
    connection: upgrade
    sec-websocket-accept: s3pplmbitxaq9kygzzhzrbk+xoo=
    
  • 连接建立:后续通信使用 websocket 帧(frame),不再是 http

🔑 关键:一次握手,长期通信

三、spring boot 整合 websocket(基础版)

场景:简易在线聊天室

1. 添加依赖(pom.xml)

<dependency>
    <groupid>org.springframework.boot</groupid>
    <artifactid>spring-boot-starter-websocket</artifactid>
</dependency>

2. 启用 websocket 支持

@configuration
@enablewebsocketmessagebroker
public class websocketconfig implements websocketmessagebrokerconfigurer {

    @override
    public void registerstompendpoints(stompendpointregistry registry) {
        // 注册 stomp 端点,允许跨域
        registry.addendpoint("/ws")
                .setallowedoriginpatterns("*")
                .withsockjs(); // 兼容不支持 websocket 的浏览器(降级到 xhr-streaming)
    }

    @override
    public void configuremessagebroker(messagebrokerregistry registry) {
        // 客户端订阅前缀
        registry.enablesimplebroker("/topic", "/queue"); // 服务端推送前缀
        registry.setapplicationdestinationprefixes("/app"); // 客户端发送消息前缀
    }
}

💡 使用 stomp(simple text oriented messaging protocol)作为子协议,简化消息路由。

3. 编写控制器(处理客户端消息)

@controller
public class chatcontroller {

    @messagemapping("/chat.sendmessage") // 客户端发送到 /app/chat.sendmessage
    @sendto("/topic/public")             // 广播给所有订阅 /topic/public 的客户端
    public chatmessage sendmessage(chatmessage chatmessage) {
        return chatmessage;
    }

    @messagemapping("/chat.adduser")
    @sendto("/topic/public")
    public chatmessage adduser(@payload chatmessage chatmessage,
                               simpmessageheaderaccessor headeraccessor) {
        // 将用户名加入会话
        headeraccessor.getsessionattributes().put("username", chatmessage.getsender());
        chatmessage.setcontent("加入聊天室");
        return chatmessage;
    }
}

4. 定义消息实体

@data
@noargsconstructor
@allargsconstructor
public class chatmessage {
    private string content;
    private string sender;
    private messagetype type; // enum: chat, join, leave

    public enum messagetype {
        chat, join, leave
    }
}

5. 前端连接(html + js)

<script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/stompjs@2.3.3/lib/stomp.min.js"></script>

<script>
const stompclient = stomp.over(new sockjs('/ws'));

stompclient.connect({}, () => {
    console.log('连接成功');

    // 订阅公共频道
    stompclient.subscribe('/topic/public', (payload) => {
        const msg = json.parse(payload.body);
        document.getelementbyid('chat').innerhtml += 
            `<div><b>${msg.sender}</b>: ${msg.content}</div>`;
    });

    // 发送消息
    function sendmessage() {
        const content = document.getelementbyid('message').value;
        stompclient.send("/app/chat.sendmessage", {}, json.stringify({
            content: content,
            sender: '张三',
            type: 'chat'
        }));
    }
});
</script>

✅ 效果:用户加入、发消息,所有在线用户实时看到。

四、高级实战用法

场景1:私聊(点对点通信)

❌ 反例:用 /topic 广播实现私聊 → 信息泄露!

✅ 正确做法:使用 用户专属队列

// 获取当前登录用户(需集成 spring security)
private string getcurrentuser(simpmessageheaderaccessor headeraccessor) {
    // 从 token / session 中解析
    return (string) headeraccessor.getsessionattributes().get("username");
}

@messagemapping("/chat.private")
public void sendprivatemessage(@payload chatmessage message,
                               simpmessageheaderaccessor headeraccessor) {
    string currentuser = getcurrentuser(headeraccessor);
    string recipient = message.getrecipient(); // 假设消息中包含接收者

    // 构造用户专属队列路径
    string destination = "/queue/messages/" + recipient;
    
    // 使用 convertandsendtouser 发送(自动加 /user 前缀)
    messagingtemplate.convertandsendtouser(recipient, "/queue/messages", message);
}

⚠️ 注意:前端需订阅 /user/queue/messages(stomp 会自动映射到用户会话)

// 前端订阅私信
stompclient.subscribe('/user/queue/messages', (payload) =&gt; {
    const msg = json.parse(payload.body);
    // 显示私聊消息
});

场景2:连接管理 & 在线人数统计

@component
public class websocketeventlistener {

    private static final set&lt;string&gt; onlineusers = concurrenthashmap.newkeyset();

    @eventlistener
    public void handlewebsocketconnectlistener(sessionconnectedevent event) {
        // 用户上线(需结合认证)
        system.out.println("用户连接: " + event.getmessage().getheaders());
    }

    @eventlistener
    public void handlewebsocketdisconnectlistener(sessiondisconnectevent event) {
        stompheaderaccessor headeraccessor = stompheaderaccessor.wrap(event.getmessage());
        string username = (string) headeraccessor.getsessionattributes().get("username");
        if (username != null) {
            onlineusers.remove(username);
            // 广播在线人数
            messagingtemplate.convertandsend("/topic/online", onlineusers.size());
        }
    }
}

可用于“好友在线状态”、“客服排队人数”等场景。

场景3:消息持久化 & 离线消息

当用户离线时,消息不能丢!

方案:

  • 发送前检查用户是否在线(维护 map<userid, sessionid>
  • 若不在线,将消息存入数据库(offline_messages 表)
  • 用户上线时,拉取离线消息并推送
public void sendprivatemessage(chatmessage message) {
    if (isuseronline(message.getrecipient())) {
        messagingtemplate.convertandsendtouser(...);
    } else {
        offlinemessageservice.save(message); // 存 db
    }
}

// 用户上线时
@eventlistener
public void onuseronline(sessionconnectedevent event) {
    string userid = getcurrentuserid(event);
    list&lt;chatmessage&gt; offlinemsgs = offlinemessageservice.getanddelete(userid);
    offlinemsgs.foreach(msg -&gt; 
        messagingtemplate.convertandsendtouser(userid, "/queue/offline", msg)
    );
}

五、注意事项 & 常见坑点

问题解决方案
跨域问题setallowedoriginpatterns("*")(生产环境应限制域名)
集群部署单机内存存储会话 → 多节点无法互通 → 需用 redis + websocket 消息广播
连接断开重连前端监听 onclose,自动重连 + 重新订阅
消息丢失关键消息需 ack 机制 + 重试(websocket 本身不保证可靠)
安全认证握手时校验 token(通过 handshakeinterceptor

示例:token 认证拦截器

public class httphandshakeinterceptor implements handshakeinterceptor {
    @override
    public boolean beforehandshake(serverhttprequest request, serverhttpresponse response,
                                   websockethandler wshandler, map&lt;string, object&gt; attributes) {
        if (request instanceof servletserverhttprequest) {
            servletserverhttprequest servletrequest = (servletserverhttprequest) request;
            string token = servletrequest.getservletrequest().getparameter("token");
            if (isvalidtoken(token)) {
                attributes.put("userid", parseuserid(token));
                return true;
            }
        }
        return false; // 拒绝连接
    }

    @override
    public void afterhandshake(serverhttprequest request, serverhttpresponse response,
                               websockethandler wshandler, exception exception) {}
}

注册到配置中:

registry.addendpoint("/ws")
        .addinterceptors(new httphandshakeinterceptor())
        .setallowedoriginpatterns("*");

六、替代方案对比

技术优点缺点适用场景
websocket真正双向、低延迟需要保持长连接聊天、实时协作、游戏
sse (server-sent events)简单、基于 http仅服务端→客户端实时通知、日志流
mqtt over websocket轻量、支持 qos需要额外 brokeriot 设备通信
grpc-web高性能、强类型浏览器支持有限内部微服务调用

七、总结

websocket 是构建实时 web 应用的核心技术,关键要点:

  • 使用 stomp + spring message broker 简化开发
  • 私聊用 /user/queue,广播用 /topic
  • 连接状态管理 实现在线人数、离线消息
  • 集群部署需 redis 广播(如 spring-boot-starter-redis + redismessagelistener
  • 安全第一:握手时校验身份,避免未授权连接

到此这篇关于websocket详解及spring boot+java高级实战用法示例的文章就介绍到这了,更多相关spring boot websocket详解内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com