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>

Related articles: