定义:
-
sockjs.js: 浏览器JavaScript库,它提供了一个类似于网络的对象。SockJS提供了一个连贯的、跨浏览器的Javascript API,它在浏览器和web服务器之间创建了一个低延迟、全双工、跨域通信通道。
-
STOMP:简单(流)文本定向消息协议,它提供了一个可互操作的连接格式,允许STOMP客户端与任意STOMP消息代理(Broker)进行交互。有点像TCP和HTTP之间的关系,在websocket的通信中,有了STOMP协议后,客户端和服务端能够以更友好的方式进行交流。如果没有提供第三方的STOMP代理,比如Rabbitmq等,那么使用的就是spring容器自己提供的STOMP代理。
关系:
能够使用sock.js建立websocket的客户端连接。websocket可以使用STOMP协议作为传输的协议。
样例代码如下:
- 定义WebSocketMessageBrokerConfigurer,一般是继承自AbstractWebSocketMessageBrokerConfigurer:
@Configuration@EnableWebSocketMessageBroker //在 WebSocket 上启用 STOMPpublic class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry config) { /** * 启用了STOMP代理中继功能:并将其目的地前缀设置为 "/topic"; * spring就能知道 所有目的地前缀为"/topic" 的消息都会发送到STOMP代理中; */ config.enableSimpleBroker("/topic", "/user"); /** * 设置了应用的前缀为"app":所有目的地以"/app"打头的消息(发送消息url not连接url) * 都会路由到带有@MessageMapping注解的方法中,而不会发布到代理队列或主题中; */ config.setApplicationDestinationPrefixes("/app"); } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/webSocket").setAllowedOrigins("*").withSockJS(); }}
- registerStompEndpoints:注册websocket的端点地址,也就是客户端连接的地址,在上面的配置中,客户端连接的地址是:
- setAllowedOrigins:上面的代码中表示是所有的ip地址都能够连接,也可以对固定的地址进行过滤。
- sockjs():提供对sockjs访问的支持。
websocket一般有两种应用场景:
- 客户端订阅到服务端,这个时候服务端会处理一些客户端的信息,比如将客户端的信息注册到服务端的列表之中。
- 服务端收到消息后进行推送到客户端。
上面的配置中:
- /app:代表客户端直接发送给服务端的消息,不需要经过stomp消息代理,将由@MessageMapping的直接处理,并返回消息。 例如:客户端发送的消息:
stompClient.send("/app/webSocket/updateDevice", {}, JSON.stringify(req));
/app/websockt/send将由下面的代码来进行处理:
@MessageMapping("/webSocket/send") public void updateDevice(SimpMessageHeaderAccessor headerAccessor, String requestContent) throws Exception { SubscriptionMsg msg = JSON.parseObject(requestContent, SubscriptionMsg.class); System.out.println(msg); }
STOMP消息代理的配置如下:
config.enableSimpleBroker("/topic", "/user");
客户端使用stompClient.subscribe("/topic/theme"),将通过STOMP订阅在这个topic的主题.
- 发送消息到单个/user通道:
@Override public void configureMessageBroker(MessageBrokerRegistry registry) {// registry.setPathMatcher(new AntPathMatcher("."));//可以已“.”来分割路径,看看类级别的@messageMapping和方法级别的@messageMapping registry.enableSimpleBroker("/topic","/user"); registry.setUserDestinationPrefix("/user/"); registry.setApplicationDestinationPrefixes("/app");//走@messageMapping }
@RequestMapping("/app")@Controllerpublic class WebSocketController { @Resource private SimpMessagingTemplate simpMessagingTemplate; @MessageMapping("/hello")// @SendTo("/topic/hello")//会把方法的返回值广播到指定主题(“主题”这个词并不合适) public void toTopic(SocketMessageVo msg , String name) { System.out.println(msg.getName()+","+msg.getMsg()); this.simpMessagingTemplate.convertAndSend("/topic/hello",msg.getName()+","+msg.getMsg());// return "消息内容:"+ msg.getName()+"--"+msg.getMsg(); } @MessageMapping("/message")// @SendToUser("/message")//把返回值发到指定队列(“队列”实际不是队列,而是跟上面“主题”类似的东西,只是spring在SendTo的基础上加了用户的内容而已) public void toUser(SocketMessageVo msg ) { System.out.println(msg.getName()+","+msg.getMsg()); this.simpMessagingTemplate.convertAndSendToUser("123","/message",msg.getName()+msg.getMsg()); } @RequestMapping("/sendMsg") public void sendMsg(HttpSession session){ System.out.println("测试发送消息:随机消息" +session.getId()); this.simpMessagingTemplate.convertAndSendToUser("123","/message","后台具体用户消息"); }}
总结一下:
WebSocketConfig 中配置setApplicationDestinationPrefixes()的消息会被转发到WebSocketController 中 @MessageMapping 相应方法进行处理。@SendTo("/topic/message") 会把方法的返回值序列化为json串,然后发送到指定的主题,不用此注解,使用 simpMessagingTemplate.convertAndSend 效果相同;若为 @SendToUser("/message") 则为发送到指定的用户队列(实际队列名字为/user/用户名/原队列名),不用此注解,使用 simpMessagingTemplate.convertAndSendToUser() 效果相同;
参考文档: