Java SpringBoot整合WebSocket 实现实时聊天,消息指定推送
代码中都是有相关的注释
首先需要配置 WebSocket 的配置类,进行相关信息的配置。
package com.njtswl.springboot.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
@Configuration
@EnableWebSocket //启用websocket
public class WebSocketConfig implements WebSocketConfigurer{
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) {
webSocketHandlerRegistry.addHandler(getHandler(),"/websocket/*").addInterceptors(new ChatIntercepter()).setAllowedOrigins("*");
//一开始没有在放上拦截器的后面加上 setAllowedOrigins("*") 打开链接直接链接超时 403
}
@Bean
public TextMessageHandler getHandler(){
return new TextMessageHandler();
}
}
接下来需要配置WebSocket 的拦截器
package com.njtswl.springboot.config;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.lang.Nullable;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;
import java.util.Map;
/**
* websocket握手的拦截器,检查握手请求和响应,对websockethandler传递属性,用于区别websocket
* */
public class ChatIntercepter extends HttpSessionHandshakeInterceptor {
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
//我们为了区别链接之前是不是通过用户名来区别是谁的,此处我们还是一样的逻辑通过名字区分
//现在我们获取到用户的名字,因为我们的地址是使用的rest风格,定义的地址是最后以为是名字,所以此处我们只需要找到请求地址拿到最后一位就行
System.out.println("握手之前");
String url = request.getURI().toString();
//如果此处 url.lastIndexOf 后面没有 +1 则会带上 /
String name = url.substring(url.lastIndexOf("/") + 1);
//给当前链接设置名字
attributes.put("name",name);//建议将name抽取为静态常量
return super.beforeHandshake(request, response, wsHandler, attributes);
}
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, @Nullable Exception ex) {
System.out.println("握手之后");
super.afterHandshake(request, response, wsHandler, ex);
}
}
配置建立链接时的处理,以及 WebSocket 的信息发送
package com.njtswl.springboot.config;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class TextMessageHandler extends TextWebSocketHandler {
//用于存放所有建立链接的对象
private Map<String,WebSocketSession> allClients = new HashMap<>();
/**
* 处理文本消息
* session 当前发送消息的用户的链接
* message 发送的消息是什么
* */
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
JSONObject jsonObject = JSON.parseObject(new String(message.asBytes()));
String to = jsonObject.getString("toUser"); //找到接收者
String toMessage = jsonObject.getString("toMessage"); //获取到发送的内容
String fromUser = (String) session.getAttributes().get("name"); //获取到当前发送消息的用户姓名
String content = "收到来自"+fromUser+"的消息,内容是:"+toMessage; //拼接的字符串
TextMessage toTextMessage = new TextMessage(content);//创建消息对象
sendMessage(to,toTextMessage); //一个封装的方法,进行点对点的发送数据
}
//发送消息的封装方法
public void sendMessage(String toUser,TextMessage message){
//获取到对方的链接
WebSocketSession session = allClients.get(toUser);//获取到对方的链接
if (session != null && session.isOpen()) {
try {
session.sendMessage(message);//发送消息
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 当链接建立的时候调用
* */
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
String name = (String) session.getAttributes().get("name");//获取到拦截器中设置的name
if (name != null) {
allClients.put(name,session);//保存当前用户和链接的关系
}
}
/**
* 当链接关闭的时候
* 这里没有做相关的代码处理
* */
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
super.afterConnectionClosed(session, status);
}
}
最后是客户端的代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<script type="text/javascript">
var websocket = null;
function connection(){
var username = document.getElementById("name").value;
if('WebSocket' in window){//支持不支持websocket
websocket = new WebSocket("ws://localhost:8080/websocket/"+username);
}else{
alert('什么破电脑不支持WebSocket,砸了吧');
}
websocket.onopen = function(){
document.getElementById("message").innerHTML = "建立链接了";
}
websocket.onmessage = function(event){
var data = event.data;
document.getElementById("message").innerHTML = data;
}
websocket.onerror = function(){
document.getElementById("message").innerHTML = "出现异常了";
}
websocket.onclose = function(){
document.getElementById("message").innerHTML = "链接关闭了";
}
window.onbeforeunload = function(){ //当浏览器的页面窗口关闭的时候,此处应该关闭链接防止服务器出现异常
if(websocket!=null){
websocket.close();
}
}
}
function sendMessage(){
//获取到发送给谁
var toUser = document.getElementById("toUser").value;
//获取到发送的内容
var toMessage = document.getElementById("toMessage").value;
if(websocket!=null){
var message = '{"toUser":"'+toUser+'","toMessage":"'+toMessage+'"}';
websocket.send(message);
}
}
</script>
<body>
<input type="text" name="name" id="name"/><button onclick="connection()">链接</button><br/>
接收者名字:<input type="text" name="toUser" id="toUser"/><br/>
内容:<input type="text" name="toMessage" id="toMessage"/><button type="button" onclick="sendMessage()">发送</button>
<span id="message"></span>
</body>
</html>
尽量边理解边敲,思考程序的执行顺序,在了解顺序之后,再思考每一步是怎么做的,了解到每一步怎么做的,再思考怎么实现的,最后为什么这样实现。
最后希望我这篇学习的文档能够帮助到你们。
这边加上用到的依赖
<!-- https://mvnrepository.com/artifact/org.springframework/spring-websocket -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>