
最近在开发使用Websocket主动推送的功能,碰到一些问题,记录一下
基本spring-boot体系,集成Websocket是比较方便的
1、引入依赖包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>2、加上配置类
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
// 注册STOMP端点,客户端将使用它连接到我们的WebSocket服务器
registry.addEndpoint("/ws")
//.addInterceptors(new OriginHandshakeInterceptor(List.of("*")))
.setAllowedOriginPatterns("*") // 允许所有来源(生产环境中应更严格)
.withSockJS() // 启用SockJS回退选项
.setClientLibraryUrl("https://cdn.bootcdn.net/ajax/libs/sockjs-client/1.6.1/sockjs.min.js");
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry)
{
//表明在topic、queue、users这三个域上可以向客户端发消息。
registry.enableSimpleBroker("/topic","/queue","/users");
//客户端向服务端发起请求时,需要以/app为前缀。
registry.setApplicationDestinationPrefixes("/app");
//给指定用户发送一对一的消息前缀是/user/。
registry.setUserDestinationPrefix("/user/");
}
}但在与前端联调时,怎么也连接不上。
前端使用nodejs写了一个后端server,发现没问题。
后端使用AI也生成了一个前端demo,也没有问题。
后来发现,原因在于前后端使用的底层框架不一样。前端使用了Socket.IO,后端使用了SocketJS
在现代Web开发中,实时通信已经成为构建动态应用不可或缺的一部分
为了在浏览器中实现这一目标,WebSocket协议被广泛采用。然而,并非所有浏览器都原生支持WebSocket,这促使开发者寻找其他解决方案以确保更广泛的兼容性。
SockJS和Socket.IO是两种流行的解决方案,它们分别以不同的方式解决了这一问题。
SockJS和Socket.IO都是强大的实时通信库,它们提供了不同的方法来解决浏览器兼容性问题。
SockJS通过模拟WebSocket的行为,并提供了对多种浏览器传输方式的支持,
而Socket.IO则通过多种回退技术提供了更为全面的解决方案。在选择适合的库时,开发者应该根据项目需求、目标用户群体以及团队的技术栈来做出决定。无论如何,SockJS和Socket.IO都是在现代Web应用中实现实时通信的强大工具,它们极大地简化了开发者的工作,并使得构建实时应用成为可能。
特性 | Spring Boot WebSocket | Socket.IO |
|---|---|---|
核心协议 | 标准 WebSocket (RFC 6455) | 自有协议(基于 WebSocket 增强) |
兼容性方案 | 第一方集成 SockJS | 内置自己的降级方案 |
高级功能 | 通过 STOMP 子协议 提供(订阅、发布、代理)。需要额外配置(如内存代理或RabbitMQ/ActiveMQ)。 | 内置(房间、自动重连、ACK等) |
API 风格 | 基于注解的编程模型 (@MessageMapping),与 Spring MVC 风格一致。 | 事件驱动 (on, emit) |
客户端需求 | 可以使用标准 WebSocket 客户端,但推荐使用 sockjs-client + stomp.js 以获得最佳体验和兼容性。 | 必须使用 Socket.IO 客户端库 |
前后端使用相同的框架协议

一直没有搞明白这两者之间的区别是什么?
后端发送消息
SimpMessagingTemplate.convertAndSendToUser(username, "/queue/notifications",message);
前端订阅
//收不到消息
client.subscribe("/user/queue/notifications", handleNotifications);
//可以收到消息
client.subscribe("/user/jack/queue/notifications", handleNotifications);这两种都是点对点,是什么区别。查了好些资料,也没有看明白。
再跑了跑demo,终于明白了其他人说的意思
1、两种的确都是点对点
2、两者的区别
/**
* 当前端发送stompClient.send("/app/hello",message) 时,后端会把消息返回
*
* 返回到当前建立连接的端点destination=/user/topic/greetings
*/
@MessageMapping("/hello")
@SendToUser("/topic/greetings")
public Greeting greetingSendToUser(HelloMessage message) throws Exception {
Thread.sleep(1000); // simulated delay
Greeting sendToUser = new Greeting("Hello user: " + HtmlUtils.htmlEscape(message.getName()) + "!");
/**
* user jack在多个终端(打开了多个浏览器窗口),只要订阅了都能收到消息
*/
messagingTemplate.convertAndSendToUser("jack", "/topic/greetings", sendToUser);
return new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!");
}2.1 @SendToUser 只会发送到当前建立连接的destination上,不需要带具体的user信息,spring内部会帮忙处理
将原来的destination拼接为/user/userID/topic/greetings,userID是客户端的SessionID。拼接结果destination=“/user/au3ev44r/topic/greetings“
前端框架应该也有类似逻辑
2.2 convertAndSendToUser 不仅发送给建立连接的终端,还会发送给订阅了这个user信息所有的终端
https://juejin.cn/post/6844903661231947784
https://www.cnblogs.com/kakarotto-chen/p/17415808.html
https://potoyang.gitbook.io/spring-in-action-v4/untitled-11/untitled-2/untitled-1