Spring boot项目整合WebSocket方法

网友投稿 956 2022-12-07

Spring boot项目整合WebSocket方法

Spring boot项目整合WebSocket方法

WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范。WebSocket API也被W3C定为标准。WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

springboot对websocket支持很友好,只需要继承webSocketHandler类,重写几个方法就可以了

这个类是对消息的一些处理,比如是发给一个人,还是发给所有人,并且前端连接时触发的一些动作

/**

* 创建一个WebSocket server

*

* @ClassName: CustomWebSocketHandler

* @Description: TODO

* @author OnlyMate

* @Date 2018年8月16日 下午3:17:34

*

*/

@Service

public class CustomWebSocketHandler extends TextWebSocketHandler implements WebSocketHandler {

private Logger logger = LoggerFactory.getLogger(CustomWebSocketHandler.class);

// 在线用户列表

private static final Map users;

// 用户标识

private static final aString CLIENT_ID = "mchNo";

static {

users = new HashMap<>();

}

@Override

public void afterConnectionEstablished(WebSocketSession session) throws Exception {

logger.info("成功建立websocket-spring连接");

String mchNo = getMchNo(session);

if (StringUtils.isNotEmpty(mchNo)) {

users.put(mchNo, session);

session.sendMessage(new TextMessage("成功建立websocket-spring连接"));

logger.info("用户标识:{},Session:{}", mchNo, session.toString());

}

}

@Override

public void handleTextMessage(WebSocketSession session, TextMessage message) {

logger.info("收到客户端消息:{}", message.getPayload());

jsONObject msgJson = JSONObject.parseObject(message.getPayload());

String to = msgJson.getString("to");

String msg = msgJson.getString("msg");

WebSocketMessage> webSocketMessageServer = new TextMessage("server:" +message);

try {

session.sendMessage(webSocketMessageServer);

if("all".equals(to.toLowerCase())) {

sendMessageToAllUsers(new TextMessage(getMchNo(session) + ":" +msg));

}else {

sendMessageToUser(to, new TextMessage(getMchNo(session) + ":" +msg));

}

} catch (IOException e) {

logger.info("handleTextMessage method error:{}", e);

}

}

@Override

public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {

if (session.isOpen()) {

session.close();

}

logger.info("连接出错");

users.remove(getMchNo(session));

}

@Override

public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {

logger.info("连接已关闭:" + status);

users.remove(getMchNo(session));

}

@Override

public boolean supportsPartialMessages() {

return false;

}

public void sendMessage(String jsonData) {

logger.info("收到客户端消息sendMessage:{}", jsonData);

JSONObject msgJson = JSONObject.parseObject(jsonData);

String mchNo = StringUtils.isEmpty(msgJson.getString(CLIENT_ID)) ? "陌生人" : msgJson.getString(CLIENT_ID);

String to = msgJson.getString("to");

String msg = msgJson.getString("msg");

if("all".equals(to.toLowerCase())) {

sendMessageToAllUsers(new TextMessage(mchNo + ":" +msg));

}else {

sendMessageToUser(to, new TextMessage(mchNo + ":" +msg));

}

}

/**

* 发送信息给指定用户

* @Title: sendMessageToUser

* @Description: TODO

* @Date 2018年8月21日 上午11:01:08

* @author OnlyMate

* @param mchNo

* @param message

* @return

*/

public boolean sendMessageToUser(String mchNo, TextMessage message) {

if (users.get(mchNo) == null)

return false;

WebSocketSession session = users.get(mchNo);

logger.info("sendMessage:{} ,msg:{}", session, message.getPayload());

if (!session.isOpen()) {

logger.info("客户端:{},已断开连接,发送消息失败", mchNo);

return false;

}

try {

session.sendMessage(message);

} catch (IOException e) {

logger.info("sendMessageToUser method error:{}", e);

return false;

}

return true;

}

/**

* 广播信息

* @Title: sendMessageToAllUsers

* @Description: TODO

* @Date 2018年8月21日 上午11:01:14

* @author OnlyMate

* @param message

* @return

*/

public boolean sendMessageToAllUsers(TextMessage message) {

boolean allSendSuccess = true;

Set mchNos = users.keySet();

WebSocketSession session = null;

for (String mchNo : mchNos) {

try {

session = users.get(mchNo);

if (session.isOpen()) {

session.sendMessage(message);

}else {

logger.info("客户端:{},已断开连接,发送消息失败", mchNo);

}

} catch (IOException e) {

logger.info("sendMessageToAllUsers method error:{}", e);

allSendSuccess = false;

}

}

return allSendSuccess;

}

/**

* 获取用户标识

* @Title: getMchNo

* @Description: TODO

* @Date 2018年8月21日 上午11:01:01

* @author OnlyMate

* @param session

* @return

*/

private String getMchNo(WebSocketSession session) {

try {

String mchNo = session.getAttributes().get(CLIENT_ID).toString();

return mchNo;

} catch (Exception e) {

return null;

}

}

}

这个类的作用就是在连接成功前和成功后增加一些额外的功能

我们希望能够把websocketSession和httpsession对应起来,这样就能根据当前不同的session,定向对websocketSession进行数据返回;在查询资料之后,发现spring中有一个-接口,HandshakeInterceptor,可以实现这个接口,来拦截握手过程,向其中添加属性

/**

* WebSocket握手时的-

* @ClassName: CustomWebSocketInterceptor

* @Description: TODO

* @author OnlyMate

* @Date 2018年8月16日 下午3:17:04

*

*/

public class CustomWebSocketInterceptor implements HandshakeInterceptor {

private Logger logger = LoggerFactory.getLogger(CustomWebSocketInterceptor.class);

/**

* 关联HeepSession和WebSocketSession,

* beforeHandShake方法中的Map参数 就是对应websocketSession里的属性

*/

@Override

public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler handler, Map map) throws Exception {

if (request instanceof ServletServerHttpRequest) {

logger.info("*****beforeHandshake******");

HttpServletRequest httpServletRequest = ((ServletServerHttpRequest) request).getServletRequest();

HttpSession session = httpServletRequest.getSession(true);

logger.info("mchNo:{}", httpServletRequest.getParameter("mchNo"));

if (session != null) {

map.put("sessionId",session.getId());

map.put("mchNo", httpServletRequest.getParameter("mchNo"));

}

}

return true;

}

@Override

public void afterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Exception e) {

logger.info("******afterHandshake******");

}

}

这个类是配置类向Spring中注入handler

/**

* websocket的配置类

* @ClassName: CustomWebSocketConfig

* @Description: TODO

* @author OnlyMate

* @Date 2018年8月16日 下午3:17:26

*

*/

@Configuration

@EnableWebSocket

public class CustomWebSocketConfig implements WebSocketConfigurer {

@Override

public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {

registry.addHandler(customWebSocketHandler(), "/webSocketBySpring/customWebSocketHandler").addInterceptors(new CustomWebSocketInterceptor()).setAllowedOrigins("*");

registry.addHandler(customWebSocketHandler(), "/sockjs/webSocketBySpring/customWebSocketHandler").addInterceptors(new CustomWebSocketInterceptor()).setAllowedOrigins("*").withSockJS();

}

@Bean

public WebSocketHandler customWebSocketHandler() {

return new CustomWebSocketHandler();

}

}

补充说明:

setAllowedOrigins("*")一定要加上,不然只有访问localhost,其他的不予许访问

setAllowedOrigins(String[] domains),允许指定的域名或IP(含端口号)建立长连接,如果只允许自家域名访问,这里轻松设置。如果不限时使用"*"号,如果指定了域名,则必须要以http或https开头

经查阅官方文档springwebsocket 4.1.5版本前默认支持跨域访问,之后的版本默认不支持跨域,需要设置

使用withSockJS()的原因:

一些浏览器中缺少对WebSocket的支持,因此,回退选项是必要的,而Spring框架提供了基于SockJS协议的透明的回退选项。

SockJS的一大好处在于提供了浏览器兼容性。优先使用原生WebSocket,如果在不支持websocket的浏览器中,会自动降为轮询的方式。

除此之外,spring也对socketJS提供了支持。

如果代码中添加了withSockJS()如下,服务器也会自动降级为轮询。

registry.addEndpoint("/coordination").withSockJS();

SockJS的目标是让应用程序使用WebSocket API,但在运行时需要在必要时返回到非WebSocket替代,即无需更改应用程序代码。

客户端与java WebSocket建立连接

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>

<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>

value="${pageContext.request.scheme}${'://'}${pageContext.request.serverName}${':'}${pageContext.request.serverPort}${pageContext.request.contextPath}" />

value="${pageContext.request.scheme}${'://'}${pageContext.request.serverName}${':'}${pageContext.request.serverPort}${pageContext.request.contextPath}" />

sessionId:

<%

HttpSession s= request.getSession();

out.println(s.getId());

%>

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Spring security权限配置与使用大全
下一篇:SpringMVC中@Valid不起效BindingResult读取不到Error信息
相关文章

 发表评论

暂时没有评论,来抢沙发吧~