springboot整合websocket-stomp
摘自官网
STOMP(简单文本导向消息传递协议)最初是为脚本语言(例如 Ruby、Python 和 Perl)创建的,用于连接到企业消息代理。它旨在解决常用消息传递模式的最小子集。STOMP 可用于任何可靠的双向流式网络协议,例如 TCP 和 WebSocket。虽然 STOMP 是一种面向文本的协议,但消息负载可以是文本或二进制。
客户端可以使用SEND或SUBSCRIBE命令发送或订阅消息,以及destination描述消息内容和接收者信息的标头。这样便可以启用一种简单的发布-订阅机制,您可以使用该机制通过代理向其他连接的客户端发送消息,或向服务器发送消息以请求执行某些工作。
引入依赖
1 |
|
配置
- 连接时校验用户信息,并返回重写的Principal
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17@Component
public class MyHandshakeHandler extends DefaultHandshakeHandler {
@Override
protected Principal determineUser(ServerHttpRequest request, WebSocketHandler wsHandler, Map<String, Object> attributes) {
if (!(request instanceof ServletServerHttpRequest)) {
return null;
}
ServletServerHttpRequest req = (ServletServerHttpRequest) request;
// 获取请求参数中携带的uid
String uid = req.getServletRequest().getParameter("uid");
if(uid == null){
throw new RuntimeException("未登录");
}
return new MyPrincipal(uid);
}
} - 用户连接,退出的操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25@Component
public class MyWebSocketHandler implements WebSocketHandlerDecoratorFactory {
@Override
public WebSocketHandler decorate(WebSocketHandler handler) {
return new WebSocketHandlerDecorator(handler) {
// 用户登录
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
String uid = session.getPrincipal().getName();
System.out.println(uid + "登陆");
super.afterConnectionEstablished(session);
}
// 用户退出
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
String uid = session.getPrincipal().getName();
System.out.println(uid + "退出");
super.afterConnectionClosed(session, closeStatus);
}
};
}
} - websocket配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39@Configuration
//开启消息代理,默认使用内置消息代理,也可以选择配置RabbitMQ等
@EnableWebSocketMessageBroker
public class WebSocketConfigurer implements WebSocketMessageBrokerConfigurer {
@Autowired
private MyHandshakeHandler myHandshakeHandler;
@Autowired
private MyWebSocketHandler myWebSocketHandler;
@Override
public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
//客户端和服务端进行连接的endpoint
stompEndpointRegistry.addEndpoint("/im/conn")
//设置连接校验
.setHandshakeHandler(myHandshakeHandler)
//跨域
.setAllowedOriginPatterns("*")
//开启sockjs
.withSockJS();
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
// 启用/user /topic两个消息前缀,消息发送的前缀,也是客户端订阅的前缀
registry.enableSimpleBroker("/user", "/topic");
// 当使用SimpMessagingTemplate#convertAndSendToUser发送消息时,客户端订阅用/user开头。
// 即一对一发送消息,使用/user为前缀订阅
registry.setUserDestinationPrefix("/user");
// 客户端端向服务端发送消息的前缀
registry.setApplicationDestinationPrefixes("/im/");
}
@Override
public void configureWebSocketTransport(WebSocketTransportRegistration registry) {
// 登陆退出的提示
registry.addDecoratorFactory(myWebSocketHandler);
}
}
服务端
定义一个消息的实体
1
2
3
4
5
6
7
8
9public class Message implements Serializable {
//发送消息的用户id
private String uid;
//接收消息的用户id
private String toUid;
//发送的文本消息
private String content;
// getter setter ...
}controller
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43@RestController
public class ImController {
// 发送消息的模板
@Autowired
private SimpMessagingTemplate simpMessagingTemplate;
/**
* 发送消息,一对一。
* @MessageMapping接收客户端/im/send2user发送来的消息。
* /im开头参考setApplicationDestinationPrefixes("/im/")。
* Principal为连接websocket校验时返回的,可以直接在参数中使用。
*/
@MessageMapping("/send2user")
public String send2user(Message message, Principal principal) {
// 获取用户的uid
String uid = principal.getName();
// 设置发送信息的uid
message.setUid(uid);
System.out.println(uid + ":" + message);
// 发送给订阅/user/{toUid}/msg的用户
// /user开头参考setUserDestinationPrefix("/user")
// 这里的toUid是接收消息用户的uid
simpMessagingTemplate.convertAndSendToUser(message.getToUid(), "msg", message);
return "success";
}
/**
* 广播消息,发送给所有订阅/topic/sys的用户。
* 也可以使用@SendTo注解,返回值为发送的消息即可。
* @SendTo是发送给订阅的用户
* 使用@GetMapping方便直接使用http请求
*/
@GetMapping("/sendAll")
// @SendTo("/topic/sys")
public String sendAll(String message) {
System.out.println("广播消息:" + message);
// 如果是群聊,根据传递参数的群聊房间号,动态拼接/topic/{房间号},前端订阅/topic/{房间号}即可
simpMessagingTemplate.convertAndSend("/topic/sys", message);
return "success";
// return msg;
}
}
客户端
1 |
|