1、首先导包
<!--websocket-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-messaging</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
2、WebSocketConfig.java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.web.socket.handler.TextWebSocketHandler;
@Configuration
@EnableWebMvc//这个标注可以不加,如果有加,要extends WebMvcConfigurerAdapter
@EnableWebSocket
public class WebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer {
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
//1.注册WebSocket
String websocket_url = "/websocket/socketServer"; //设置websocket的地址
registry.addHandler(webSocketHandler(), websocket_url). //注册Handler
addInterceptors(new WebSocketHandshakeInterceptor()); //注册Interceptor
//2.注册SockJS,提供SockJS支持(主要是兼容ie8)
String sockjs_url = "/sockjs/socketServer"; //设置sockjs的地址
registry.addHandler(webSocketHandler(), sockjs_url). //注册Handler
addInterceptors(new WebSocketHandshakeInterceptor()). //注册Interceptor
withSockJS(); //支持sockjs协议
}
@Bean
public TextWebSocketHandler webSocketHandler() {
return new WebSocketHandler();
}
}
3、WebSocketHandler.java
import com.jam.pojo.WebSocketUser;
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.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class WebSocketHandler extends TextWebSocketHandler {
// 已建立连接的用户
private static final ArrayList<WebSocketSession> users = new ArrayList<WebSocketSession>();
SimpleDateFormat sdf = new SimpleDateFormat("MM-dd HH:mm:ss");
/**
* 处理前端发送的文本信息 js调用websocket.send时候,会调用该方法
*
* @param session
* @param message
* @throws Exception
*/
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
String username = (String) session.getAttributes().get("WEBSOCKET_USERNAME");
// 获取提交过来的消息详情
System.out.println("收到用户 " + username + " 的消息:" + message.toString());
// 分割成id和信息内容
String[] messageInfo = message.getPayload().split("@");
if (messageInfo.length != 2) {
} else {
String target = messageInfo[0];
String content = messageInfo[1];
// 遍历所有已连接用户
for (WebSocketSession user : users) {
if (user.getAttributes().get("WEBSOCKET_USERNAME").equals(target)) {
//遇到匹配用户 连接正常则发送消息
if (user.isOpen()) {
sendMessageToUser(target, new TextMessage("来自\""+username+"\"的消息:"+content+"----"+sdf.format(new Date())));
}else{//若异常则发送失败
sendMessageToUser(username, new TextMessage("对方在线异常,发送失败"+"----"+sdf.format(new Date())));
}
return;
}
}
//未找到匹配用户 发送失败
sendMessageToUser(username, new TextMessage("对方暂时不在线"+"----"+sdf.format(new Date())));
}
}
/**
* 当新连接建立的时候,被调用 连接成功时候,会触发页面上onOpen方法
*
* @param session
* @throws Exception
*/
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
users.add(session);
String username = (String) session.getAttributes().get("WEBSOCKET_USERNAME");
System.out.println("用户 " + username + " Connection Established");
session.sendMessage(new TextMessage(username + " connect"));
}
/**
* 当连接关闭时被调用
*
* @param session
* @param status
* @throws Exception
*/
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
String username = (String) session.getAttributes().get("WEBSOCKET_USERNAME");
System.out.println("用户 " + username + " Connection closed. Status: " + status);
users.remove(session);
}
/**
* 传输错误时调用
*
* @param session
* @param exception
* @throws Exception
*/
@Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
String username = (String) session.getAttributes().get("WEBSOCKET_USERNAME");
if (session.isOpen()) {
session.close();
}
System.out.println("用户: " + username + " websocket connection closed......");
users.remove(session);
}
/**
* 给所有在线用户发送消息
*
* @param message
*/
public void sendMessageToUsers(TextMessage message) {
for (WebSocketSession user : users) {
try {
if (user.isOpen()) {
user.sendMessage(message);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 在线用户数
*
*/
public List<WebSocketUser> onlineUsers() {
List<WebSocketUser> userList = new ArrayList<WebSocketUser>();
for (WebSocketSession user : users) {
String username = (String) user.getAttributes().get("WEBSOCKET_USERNAME");
userList.add(new WebSocketUser(username));
}
return userList;
}
/**
* 给某个用户发送消息
*
* @param userName
* @param message
*/
public void sendMessageToUser(String userName, TextMessage message) {
for (WebSocketSession user : users) {
if (user.getAttributes().get("WEBSOCKET_USERNAME").equals(userName)) {
try {
if (user.isOpen()) {
user.sendMessage(message);
}
} catch (IOException e) {
e.printStackTrace();
}
break;
}
}
}
}
4、WebSocketHandshakeInterceptor.java
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;
import javax.servlet.http.HttpSession;
import java.util.Map;
public class WebSocketHandshakeInterceptor implements HandshakeInterceptor {
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Map<String, Object> attributes) throws Exception {
if (request instanceof ServletServerHttpRequest) {
ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
HttpSession session = servletRequest.getServletRequest().getSession(false);
if (session != null) {
String userName = (String) session.getAttribute("SESSION_USERNAME"); //这边获得登录时设置的唯一用户标识
if (userName == null) {
userName = "未知" + session.getId();
}
attributes.put("WEBSOCKET_USERNAME", userName); //将用户标识放入参数列表后,下一步的websocket处理器可以读取这里面的数据
}
}
return true;
}
public void afterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Exception e) {
System.out.println("After Handshake");
}
}
5、WebSocketController.java
import com.alibaba.fastjson.JSON;
import com.jam.pojo.WebSocketUser;
import com.jam.utils.Retmsg;
import com.jam.websocket.WebSocketHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.socket.TextMessage;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
@RestController
@RequestMapping(value="/websocket",produces = "application/json; charset=utf-8")
public class WebSocketController {
@Bean // 这个注解会从Spring容器拿出Bean
public WebSocketHandler infoHandler() {
return new WebSocketHandler();
}
WebSocketHandler ws = new WebSocketHandler();
SimpleDateFormat sdf = new SimpleDateFormat("MM/dd HH:mm:ss");
//登录WebSocket保存用户名
@RequestMapping("/login")
public String login(String username,HttpServletRequest request) throws Exception {
System.out.println(username + "登录");
HttpSession session = request.getSession();
session.setAttribute("SESSION_USERNAME", username);
return Retmsg.toJsonMsg("登录成功");
}
//推送给所有用户的后台接口
@RequestMapping("/sendMsgToAllUsers")
public String sendMsgToAllUsers(String username,String str){
String date = sdf.format(new Date());
str+="----"+date+"[来自 \""+username+"\" 的群发信息]";
ws.sendMessageToUsers(new TextMessage(str));
return Retmsg.toJsonMsg("发送成功");
}
//获取当前在线用户名
@RequestMapping("/onlineUsers")
public @ResponseBody String onlineUsers(){
List<WebSocketUser> users = ws.onlineUsers();
return JSON.toJSONString(users);
}
}
5、JavaScript+心跳包发送
<script type="text/javascript">
var websocket = null;
var url = window.location.host;
var url2 = window.location.href;
var username = url2.split("=")[1];
var website = "http://"+url+"/websocket/sendMsgToAllUsers";
if ('WebSocket' in window) {
//Websocket的连接
websocket = new WebSocket("ws://"+url+"/websocket/socketServer");//WebSocket对应的地址
}
else if ('MozWebSocket' in window) {
//Websocket的连接
websocket = new MozWebSocket("ws://"+url+"/websocket/socketServer");//SockJS对应的地址
}
else {
//SockJS的连接
websocket = new SockJS("http://"+url+"/sockjs/socketServer"); //SockJS对应的地址
}
websocket.onopen = onOpen;
websocket.onmessage = onMessage;
websocket.onerror = onError;
websocket.onclose = onClose;
function onOpen(openEvt) {
//alert(openEvt.Data);
heartCheck.reset().start();
}
function onMessage(evt) {
$("#content").append(evt.data+"<br>"); // 接收后台发送的数据
heartCheck.reset().start();
}
function onError() {
}
function onClose() {
}
//给指定用户发消息
function doSend() {
if (websocket.readyState == websocket.OPEN) {
websocket.send($("#targetName").val()+"@"+$("#inputMsg").val());//调用后台handleTextMessage方法
alert("发送成功!");
} else {
alert("连接失败!"+websocket.readyState);
}
}
//给所有用户发消息
function doSend2(){
if (websocket.readyState == websocket.OPEN) {
$.get(website,{username:username,str:$("#inputMsg2").val()});
alert("发送成功!");
} else {
alert("连接失败!"+websocket.readyState);
}
}
window.close = function () {
websocket.onclose();
}
var heartCheck = {
timeout: 55000, // 9分钟发一次心跳,比server端设置的连接时间稍微小一点,在接近断开的情况下以通信的方式去重置连接时间。
serverTimeoutObj: null,
reset: function(){
clearTimeout(this.timeoutObj);
clearTimeout(this.serverTimeoutObj);
return this;
},
start: function(){
var self = this;
this.serverTimeoutObj = setInterval(function(){
if(websocket.readyState == 1){
console.log("连接状态,发送消息保持连接");
websocket.send("heartCheck");
heartCheck.reset().start(); // 如果获取到消息,说明连接是正常的,重置心跳检测
}else{
console.log("断开状态,尝试重连");
new WebSocket("ws://"+url+"/websocket/socketServer");
}
}, this.timeout)
}
}
</script>
评论区