What is the WebSocket

WebSocket is a protocol for full duplex communication over a single TCP connection……………..

Why implement handshake monitoring management

“Broken pipe” will cause an error if the connection is created randomly and left unconnected

On the surface, an error is reported and there is no functional defect. However, the number of requests increases and the system crashes easily. Let me focus on this side.

There are many reasons for the occurrence. At present, the reason for my occurrence is that the client has closed the connection and the server continues to push.

How to use

The webSocket integrated with SpringBoot will be used next

Import the Maven

First, the SpringBoot version

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.8. RELEASE</version>
    </parent>
Copy the code

Integrated websocket

// Add a web integration<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
	    <groupId>org.springframework.boot</groupId>
	    <artifactId>spring-boot-starter-websocket</artifactId>
	</dependency>
Copy the code

Java code

The Config configuration

First, we need to rewrite WebSocketHandlerDecoratorFactory

It is mainly used to monitor the client to come in and close the connection with a handshake

code

Need a class to manage sockets

package com.li.manager;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.socket.WebSocketSession;

import java.util.concurrent.ConcurrentHashMap;

/** * Socket manager */
@Slf4j
public class SocketManager {
    private static ConcurrentHashMap<String, WebSocketSession> manager = new ConcurrentHashMap<String, WebSocketSession>();

    public static void add(String key, WebSocketSession webSocketSession) {
        log.info("New webSocket connection {}", key);
        manager.put(key, webSocketSession);
    }

    public static void remove(String key) {
        log.info("Remove webSocket connection {}", key);
        manager.remove(key);
    }

    public static WebSocketSession get(String key) {
        log.info("Get webSocket connection {}", key);
        returnmanager.get(key); }}Copy the code
package com.li.factory;

import com.li.manager.SocketManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.WebSocketHandlerDecorator;
import org.springframework.web.socket.handler.WebSocketHandlerDecoratorFactory;

import java.security.Principal;

/** * The server and the client are executed when they are shaking hands
@Component
@Slf4j
public class WebSocketDecoratorFactory implements WebSocketHandlerDecoratorFactory {
    @Override
    public WebSocketHandler decorate(WebSocketHandler handler) {
        return new WebSocketHandlerDecorator(handler) {
            @Override
            public void afterConnectionEstablished(WebSocketSession session) throws Exception {
                log.info(SessionId = {}, session.getId());
                Principal principal = session.getPrincipal();
                if(principal ! =null) {
                    log.info("Key = {} save", principal.getName());
                    // The socket connection is cached
                    SocketManager.add(principal.getName(), session);
                }


                super.afterConnectionEstablished(session);
            }

            @Override
            public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
                log.info(SessionId = {}, session.getId());
                Principal principal = session.getPrincipal();
                if(principal ! =null) {
                    // Identity verification succeeded. Socket connection removed
                    SocketManager.remove(principal.getName());
                }
                super.afterConnectionClosed(session, closeStatus); }}; }}Copy the code
Session variables getId() and getPrincipal().getName()

GetId () : returns a unique session identifier.

GetPrincipal () : Returns Principal instance if authenticated, null if not authenticated

Principal: The abstract concept of the Principal, which can be the company ID, name, user’s unique identification token, etc

When you use the code above, you will find that getPrincipal() returns NULL. Why? I also need to rewrite a DefaultHandshakeHandler here

code
package com.li.handler;

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.support.DefaultHandshakeHandler;

import javax.servlet.http.HttpServletRequest;
import java.security.Principal;
import java.util.Map;

/** * We can request information, such as token, or session, to determine whether the user can connect, so as to prevent illegal users */
@Slf4j
@Component
public class PrincipalHandshakeHandler extends DefaultHandshakeHandler {
    @Override
    protected Principal determineUser(ServerHttpRequest request, WebSocketHandler wsHandler, Map<String, Object> attributes) {
        / * * * side can according to your requirements, how to obtain the only value, get the value of the unicode * already, can deal with the connection attributes, in listening WebSocketSession. Both getPrincipal (). The getName () * can realize the Principal () * /
        if (request instanceof ServletServerHttpRequest) {
            ServletServerHttpRequest servletServerHttpRequest = (ServletServerHttpRequest) request;
            HttpServletRequest httpRequest = servletServerHttpRequest.getServletRequest();
            /** * get your most familiar stranger, take parameters, you can cookie, request header, or URL carry, here I use url carry */
            final String token = httpRequest.getParameter("token");
            if (StringUtils.isEmpty(token)) {
                return null;
            }
            return new Principal() {
                @Override
                public String getName(a) {
                    returntoken; }}; }return null; }}Copy the code

We have everything we need. Let’s get ready to load

code
package com.li.config;

import com.li.factory.WebSocketDecoratorFactory;
import com.li.handler.PrincipalHandshakeHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketTransportRegistration;


/** * WebSocketConfig config */
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

    @Autowired
    private WebSocketDecoratorFactory webSocketDecoratorFactory;
    @Autowired
    private PrincipalHandshakeHandler principalHandshakeHandler;

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        /** * myUrl means that your front end should map */ to the URL
        registry.addEndpoint("/myUrl")
                .setAllowedOrigins("*")
                .setHandshakeHandler(principalHandshakeHandler)
                .withSockJS();
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        /** * queue point-to-point topic broadcast user point-to-point prefix */
        registry.enableSimpleBroker("/queue"."/topic");
        registry.setUserDestinationPrefix("/user");
    }

    @Override
    public void configureWebSocketTransport(WebSocketTransportRegistration registration) {
        registration.addDecoratorFactory(webSocketDecoratorFactory);
        super.configureWebSocketTransport(registration); }}Copy the code

It’s finally done

Finally, a request is made via HTTP and sent to the client

code
package com.li.controller;

import com.li.manager.SocketManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.socket.WebSocketSession;

import java.util.Map;

@RestController
@Slf4j
public class TestController {

    @Autowired
    private SimpMessagingTemplate template;

    Var socket = new SockJS(host+'/myUrl' +'? token=1234'); * /
    @RequestMapping("/sendUser")
    public void sendUser(String token) {
        log.info("Token = {}, send hello to it", token);
        WebSocketSession webSocketSession = SocketManager.get(token);
        if(webSocketSession ! =null) {
            /** * prevents broken pipe */
            template.convertAndSendToUser(token, "/queue/sendUser"."Hello"); }}/** * broadcasts, the server actively pushes to the connected client */
    @RequestMapping("/sendTopic")
    public void sendTopic(a) {
        template.convertAndSend("/topic/sendTopic"."Good evening, everyone.");

    }

    /** * The client sends the message, and the server receives the message@param message
     */
    // RequestMapping
    @MessageMapping("/sendServer")
    public void sendServer(String message) {
        log.info("message:{}", message);
    }

    /** * The client sends a message, and everyone receives it, which is equivalent to live speaking **@param message
     * @return* /
    @MessageMapping("/sendAllUser")
    @SendTo("/topic/sendTopic")
    public String sendAllUser(String message) {
        // The template mode can also be used
        return message;
    }

    /** * point-to-point user chat (); /** * point-to-point user chat ()@RequestBodyVar socket = new SockJS(host+'/myUrl' +'? token=4567'); Token Indicates the specified name *@param map
     */
    @MessageMapping("/sendMyUser")
    public void sendMyUser(@RequestBody Map<String, String> map) {
        log.info("map = {}", map);
        WebSocketSession webSocketSession = SocketManager.get(map.get("name"));
        if(webSocketSession ! =null) {
            log.info("sessionId = {}", webSocketSession.getId());
            template.convertAndSendToUser(map.get("name"), "/queue/sendUser", map.get("message")); }}}Copy the code

The front-end code

You can start it directly


      
<html>
<head>
<meta charset="UTF-8" />
<title>Spring Boot WebSocket+ Broadcast</title>
</head>
<body>
    <noscript>
        <h2 style="color:#ff0000">It appears that your browser does not support Websocket</h2>
    </noscript>
    <div>
        <div>
            <button id="connect" onclick="connect()">The connection</button>
            <button id="disconnect"  onclick="disconnect();">disconnect</button>
        </div>
        <div id="conversationDiv">
            <label>Enter your name</label> <input type="text" id="name" />
            <br>
            <label>The input message</label> <input type="text" id="messgae" />
            <button id="send" onclick="send();">send</button>
            <p id="response"></p>
        </div>
    </div>
    <script src="https://cdn.bootcss.com/sockjs-client/1.1.4/sockjs.min.js"></script>
    <script src="https://cdn.bootcss.com/stomp.js/2.3.3/stomp.min.js"></script>
    <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
    <script type="text/javascript">
        var stompClient = null;
        //gateway Indicates the gateway address
        var host="http://127.0.0.1:8888";
        function setConnected(connected) {
            document.getElementById('connect').disabled = connected;
            document.getElementById('disconnect').disabled = ! connected;document.getElementById('conversationDiv').style.visibility = connected ? 'visible' : 'hidden';
            $('#response').html();
        }
		// SendUser ***********************************************
        function connect() {
            // address + endpoint path, build webSocket link address, note that the corresponding config addEndpoint
            var socket = new SockJS(host+'/myUrl' + '? token=4567');
            stompClient = Stomp.over(socket);
            stompClient.connect({}, function(frame) {
                setConnected(true);
                console.log('Connected:' + frame);
                // Listen to the path and callback
                stompClient.subscribe('/user/queue/sendUser'.function(response) {
                    showResponse(response.body);
                });
            });
        }
		Var socket = new SockJS(host+'/myUrl'); /* function connect() {addsocket = new SockJS(host+'/myUrl'); stompClient = Stomp.over(socket); stompClient.connect({}, function(frame) { setConnected(true); console.log('Connected:' + frame); Stompclient. subscribe('/topic/sendTopic', function(response) {showResponse(response.body); }); }); } * /
        function disconnect() {
            if(stompClient ! =null) {
                stompClient.disconnect();
            }
            setConnected(false);
            console.log("Disconnected");
        }
        function send() {
            var name = $('#name').val();
            var message = $('#messgae').val();
            /*// The path where the client sends the message to the server stompClient.send("/sendServer", {}, message); * /
			// sendTopic stompClient.send("/sendAllUser", {}, message); // sendTopic stompClient.send("/sendAllUser", {}, message); * /
			Token =1234, token=4567 * then you can send */ to the specified user by writing name as token
			stompClient.send("/sendMyUser", {}, JSON.stringify({name:name,message:message}));
        }
        function showResponse(message) {
            var response = $('#response');
            response.html(message);
        }
    </script>
</body>
</html>
Copy the code

Mind you, try it yourself. A good memory is better than a bad pen.

Links to personal blog postswww.ccode.live/bertonlee/l…

The source code:Github.com/bertonlee/w…Welcome to Star

Welcome to attention

Welcome to pay attention to the public account “code development”, share the latest technical information every day