The Spring boot project integrates the WebSocket approach
- 2021-11-14 05:50:31
- OfStack
WebSocket is a protocol for full duplex communication over a single TCP connection. WebSocket communication protocol was designated as standard RFC 6455 by IETF in 2011, and supplemented by RFC 7936. WebSocket API is also standard by W3C. WebSocket makes data exchange between the client and the server easier, allowing the server to actively push data to the client. In WebSocket API, the browser and server only need to complete one handshake, and they can directly create a persistent connection and carry out bidirectional data transmission.
springboot is very friendly to websocket support, just inherit the webSocketHandler class and override a few methods
This class does 1 processing of a message, such as whether it is sent to 1 person or to everyone, and 1 action triggered when the front end connects
/**
* Create 1 A WebSocket server
*
* @ClassName: CustomWebSocketHandler
* @Description: TODO
* @author OnlyMate
* @Date 2018 Year 8 Month 16 Day Afternoon 3:17:34
*
*/
@Service
public class CustomWebSocketHandler extends TextWebSocketHandler implements WebSocketHandler {
private Logger logger = LoggerFactory.getLogger(CustomWebSocketHandler.class);
// Online user list
private static final Map<String, WebSocketSession> users;
// User ID
private static final aString CLIENT_ID = "mchNo";
static {
users = new HashMap<>();
}
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
logger.info(" Successfully established websocket-spring Connect ");
String mchNo = getMchNo(session);
if (StringUtils.isNotEmpty(mchNo)) {
users.put(mchNo, session);
session.sendMessage(new TextMessage(" Successfully established websocket-spring Connect "));
logger.info(" User ID: {} , Session : {}", mchNo, session.toString());
}
}
@Override
public void handleTextMessage(WebSocketSession session, TextMessage message) {
logger.info(" Client message received: {}", 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(" Connection error ");
users.remove(getMchNo(session));
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
logger.info(" Connection closed: " + status);
users.remove(getMchNo(session));
}
@Override
public boolean supportsPartialMessages() {
return false;
}
public void sendMessage(String jsonData) {
logger.info(" Client message received sendMessage : {}", jsonData);
JSONObject msgJson = JSONObject.parseObject(jsonData);
String mchNo = StringUtils.isEmpty(msgJson.getString(CLIENT_ID)) ? " Stranger " : 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));
}
}
/**
* Send a message to a specified user
* @Title: sendMessageToUser
* @Description: TODO
* @Date 2018 Year 8 Month 21 Day Morning 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(" Client :{}, Disconnected, message sending failed ", mchNo);
return false;
}
try {
session.sendMessage(message);
} catch (IOException e) {
logger.info("sendMessageToUser method error : {}", e);
return false;
}
return true;
}
/**
* Broadcast information
* @Title: sendMessageToAllUsers
* @Description: TODO
* @Date 2018 Year 8 Month 21 Day Morning 11:01:14
* @author OnlyMate
* @param message
* @return
*/
public boolean sendMessageToAllUsers(TextMessage message) {
boolean allSendSuccess = true;
Set<String> mchNos = users.keySet();
WebSocketSession session = null;
for (String mchNo : mchNos) {
try {
session = users.get(mchNo);
if (session.isOpen()) {
session.sendMessage(message);
}else {
logger.info(" Client :{}, Disconnected, message sending failed ", mchNo);
}
} catch (IOException e) {
logger.info("sendMessageToAllUsers method error : {}", e);
allSendSuccess = false;
}
}
return allSendSuccess;
}
/**
* Get User Identity
* @Title: getMchNo
* @Description: TODO
* @Date 2018 Year 8 Month 21 Day Morning 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;
}
}
}
The function of this class is to add 1 extra function before and after the connection is successful
We hope to be able to correspond websocketSession and httpsession, so that we can return data to websocketSession according to different session at present; After inquiring the data, we find that there is an interceptor interface in spring, HandshakeInterceptor, which can be implemented to intercept the handshake process and add attributes to it
/**
* WebSocket Interceptor when shaking hands
* @ClassName: CustomWebSocketInterceptor
* @Description: TODO
* @author OnlyMate
* @Date 2018 Year 8 Month 16 Day Afternoon 3:17:04
*
*/
public class CustomWebSocketInterceptor implements HandshakeInterceptor {
private Logger logger = LoggerFactory.getLogger(CustomWebSocketInterceptor.class);
/**
* Association HeepSession And WebSocketSession ,
* beforeHandShake Method in the Map Parameter Is the corresponding websocketSession Attributes in
*/
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler handler, Map<String, Object> 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******");
}
}
This class is the configuration class that injects handler into Spring
/**
* websocket Configuration class of
* @ClassName: CustomWebSocketConfig
* @Description: TODO
* @author OnlyMate
* @Date 2018 Year 8 Month 16 Day Afternoon 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();
}
}
Supplementary notes:
setAllowedOrigins ("*") 1 must be added, otherwise only localhost will be accessed, and others will not be accessed
setAllowedOrigins (String [] domains), which allows the specified domain name or IP (including port number) to establish long connections. If only your own domain name is allowed to access, it is easy to set here. If the "*" sign is used indefinitely, if the domain name is specified, it must start with http or https
After consulting the official document springwebsocket version 4.1. 5, cross-domain access is supported by default, and later versions do not support cross-domain by default, which needs to be set
Reasons for using withSockJS ():
1 Some browsers lack support for WebSocket, so the fallback option is necessary, while the Spring framework provides transparent fallback options based on the SockJS protocol.
One of the great benefits of SockJS is that it provides browser compatibility. Priority is given to native WebSocket, which will be automatically reduced to polling in browsers that do not support websocket.
In addition, spring also provides support for socketJS.
If withSockJS () is added to the code as follows, the server will automatically be downgraded to polling.
registry.addEndpoint("/coordination").withSockJS();
The goal of SockJS is for applications to use WebSocket API, but at runtime you need to return to a non-WebSocket replacement if necessary, that is, you don't need to change the application code.
The client establishes a connection with 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"%>
<c:set var="ctx" value="${pageContext.request.contextPath}" />
<c:set var="ctxpath"
value="${pageContext.request.scheme}${'://'}${pageContext.request.serverName}${':'}${pageContext.request.serverPort}${pageContext.request.contextPath}" />
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta charset=UTF-8">
<title> Login test </title>
</head>
<body>
<h2>Hello World! Web Socket by Spring</h2>
<div>
<span>sessionId:</span>
<%
HttpSession s= request.getSession();
out.println(s.getId());
%>
</div>
<input id="sessionId" type="hidden" value="<%=session.getId() %>" />
<input id="text" type="text" />
<button onclick="send()"> Send a message </button>
<hr />
<button onclick="closeWebSocket()"> Shut down WebSocket Connect </button>
<hr />
<div id="message"></div>
</body>
<script type="text/javascript" src="http://localhost:8088/static/js/sockjs-0.3.min.js"></script>
<script type="text/javascript">
var websocket = null;
// Determine whether the current browser supports WebSocket
// Determine whether the current browser supports WebSocket
if('WebSocket' in window) {
websocket = new WebSocket("ws://localhost:8088/websocket/webSocketBySpring/customWebSocketHandler?mchNo="+ 123);
} else if('MozWebSocket' in window) {
websocket = new MozWebSocket("ws://localhost:8088/websocket/webSocketBySpring/customWebSocketHandler?mchNo="+ 123);
} else {
websocket = new SockJS("http://localhost:8088/websocket/sockjs/webSocketBySpring/customWebSocketHandler?mchNo="+ 123);
}
// Callback method with connection error
websocket.onerror = function () {
setMessageInnerHTML("WebSocket Connection error occurred ");
};
// Callback method for successful connection establishment
websocket.onopen = function () {
setMessageInnerHTML("WebSocket Connection succeeded ");
}
// Callback method for receiving message
websocket.onmessage = function (event) {
setMessageInnerHTML(event.data);
}
// Callback method for connection closure
websocket.onclose = function () {
setMessageInnerHTML("WebSocket Connection closed ");
}
// Listen for window closing events, and when the window is closed, take the initiative to close it websocket Connection to prevent the window from being closed before the connection is disconnected. server End will throw an exception.
window.onbeforeunload = function () {
closeWebSocket();
}
// Display messages on Web pages
function setMessageInnerHTML(innerHTML) {
document.getElementById('message').innerHTML += innerHTML + '<br/>';
}
// Shut down WebSocket Connect
function closeWebSocket() {
websocket.close();
}
// Send a message
function send() {
var message = document.getElementById('text').value;
websocket.send(message);
}
</script>
</html>