一 名词解释
1.WebSocket
WebSocket 是发送和接收消息的底层API,WebSocket 协议提供了通过一个套接字实现全双工通信的功能。也能够实现 web 浏览器和 server 间的异步通信,全双工意味着 server 与浏览器间可以发送和接收消息。需要注意的是必须考虑浏览器是否支持
2.SockJs
为了应对许多浏览器不支持WebSocket协议的问题,设计了备选SockJs。
SockJS 是 WebSocket 技术的一种模拟。SockJS 会 尽可能对应 WebSocket API,但如果 WebSocket 技术不可用的话,就会选择另外的通信方式协议。
3.STOMP
SockJS 为 WebSocket 提供了 备选方案。但无论哪种场景,对于实际应用来说,这种通信形式层级过低。下面看一下如何 在 WebSocket 之上使用 STOMP协议,来为浏览器 和 server 间的 通信增加适当的消息语义。(STOMP—— Simple Text Oriented Message Protocol——面向消息的简单文本协议)
4.三者之间的关系
WebSocket 是底层协议,SockJS 是WebSocket 的备选方案,也是 底层协议,而 STOMP 是基于 WebSocket(SockJS) 的上层协议
二 springboot的config
<!-- websocket-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
1.基础配置
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfiguration implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/topic", "/queue", "/user"); //表示在topic和queue这两个域上服务端可以向客户端发消息
registry.setApplicationDestinationPrefixes("/app");//客户端向服务器端发送时的主题上面需要加"/app"作为前缀
registry.setUserDestinationPrefix("/user");//指定用户发送一对一的主题,前缀是"/user"
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/stomp/endpoint").withSockJS();
}
}
第一步就是添加注解 此注解 这个配置类不仅配置了 WebSocket,还配置了基于代理的 STOMP 消息
第二步 配置一个消息代理..一个是允许服务器向客户端发送的域 一个是客户端想服务器发送的前缀,最后一个是指定一对第一发送的前缀
第三步 就是配置了节点,并且使用sockjs的方式
2.监听连接与断开
用到了redis管理session
@Access(name = "监听websocket连接成功")
@EventListener
public void onConnectEvent(SessionConnectEvent event) {
//获取Session连接信息
StompHeaderAccessor sha = StompHeaderAccessor.wrap(event.getMessage());
//获取SessionId
String sessionId = sha.getSessionId();
//存储redis
Map<String, String> map = new HashMap<String, String>();
map.put(sessionId, agentNo);
redisTemplate.opsForHash().putAll("webSocket", map);
}
@Access(name = "监听websocket断开连接")
@EventListener
public void onDisconnectEvent(SessionDisconnectEvent event) {
//获取Session连接信息
StompHeaderAccessor sha = StompHeaderAccessor.wrap(event.getMessage());
//获取SessionId
String sessionId = sha.getSessionId();
//redis删除此sessionId
redisTemplate.opsForHash().delete("webSocket", sessionId);
}
}
三.实现前端代码
1.引入js
<!-- websocket ****代表自己的项目路径 这个写自己的 --> <script src="/***/sockjs-client/sockjs.min.js"></script> <script src="/***/stomp-websocket/stomp.min.js"></script> <!-- 参考 --> <!-- https://cdn.bootcdn.net/ajax/libs/sockjs-client/1.4.0/sockjs.min.js https://cdn.bootcdn.net/ajax/libs/stomp.js/2.3.3/stomp.min.js -->
2.连接和订阅消息
使用了agentNo 区分订阅广播消息与个人消息
var stompClient = null;
var socket = null;
function connect() {
//从sesionStorage获取坐席工号 下面连接头需要 这个按照自己需求修改
agentNo = sessionStorage.getItem("agentNo");
//建立连接对象(还未发起连接) 地址更换成自己的 连接服务器注册端点endPoint时,写访问服务器的全路径URL:
socket = new SockJS('地址');
//获取 STOMP 子协议的客户端对象
stompClient = Stomp.over(socket);
//建立连接,并且连接头上定义了坐席工号
stompClient.connect({
userName: agentNo // <---这是头 在后面的监听 连接成功的类可以获取到 业务代码需要 按需删除
}, function (frame) {
console.log('Connected: ' + frame);
//订阅广播消息
stompClient.subscribe('/topic/greetings', function (greeting) {
var data = JSON.parse(greeting.body).data;
//实现自己的需求
});
//订阅个人消息
stompClient.subscribe('/user/' + agentNo + '/message', function (greeting) {
var data = JSON.parse(greeting.body);
//实现自己的需求
});
});
}
2.发送消息
function send(data) {
var param = {
id: data,
release: true
}
stompClient.send("/app/release", {}, JSON.stringify(param));
// <----- app 就是在config中定义的客户端往服务器发送的前缀,param是信息.必须用实体发送后台必须用实体接收
//release是后面controller中定义的 相当于RequestMapping中写的地址一样
}
四.实现后端代码
1.发送广播
@Access(name = "发布公告")
@MessageMapping("/release")
@SendTo("/topic/greetings")
public WebResult release(@RequestBody Announcement announcement) {
//执行业务代码逻辑
}
2.发送个人消息
@Autowired
private SimpMessagingTemplate messagingTemplate;
public void demo() {
// messagingTemplate.convertAndSend(url,""); //方法1
//方法2 相当于/user/' + agentNo + '/message' 对应前端订阅的地址
messagingTemplate.convertAndSendToUser(ledgerQuery.getAgentNo(), "/message", "");
}