springboot integrates the original version of websocket

Keywords: Session SpringBoot less Spring

Articles Catalogue


HTTP requests are used in one of the most widely developed protocols between us and users. In HTTP, we can simply get the content (pages) we need through a browser. But he also has his limitations. Today our protagonist, websocket, will show off his functions

HTTP Disadvantages

  • HTTP can only have client to initiate the request server to respond and return the result. The server can't send information to the client on its own initiative. So some websites use page timer function to solve the real-time problem. In short, the client sends requests to the server on a regular basis.
    Such a waste of resources is caused by more or less.

  • HTTP is memoryless. Every time the server requests, it is impossible to understand the behavior of the client before, but we usually browse the website when the browser feels that the browser knows what we did before. This is the data provided by these servers when the website requests cookie s that are added. For us, we feel like we have memories. But it's not.

  • After HTTP 1.1, short connection and long connection are adopted. HTTP requests also require three handshakes at a time. So each connection consumes resources. After 1.1, HTTP actually uses long connections for a certain period of time, which can reduce the overhead of resources.

  • The long connection mentioned above may be questionable. In fact, HTTP protocol is based on TCP protocol. So naturally there are long connections.

HTTP websocket distinction

  • HTTP is memory-free because of its short connections. To solve this problem, each request consists of General+Request Head+Request Paylaod+Response Headers. Heads are what browsers need to remember, and they consume performance every time they pass around.

  • Because websocket is a long connection feature, one connection can always communicate in two directions. From the carrier point of view, websocket pays less attention and only needs to communicate the information currently needed. Both sides have historical information.

websocket principle

Use scenarios

springboot integrates websocket

Environmental preparation

  • Introducing jar of websocket based on spring boot
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

  • Then add the following configuration to the project
@Configuration
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

  • Then we can write events such as receiving and sending websockets. I encapsulated it on this basis. First, I abstracted a websocket.
public abstract class BaseWebSocket extends  BaseController{
    /**
     * Static variables that record the number of current online connections. It should be designed to be thread-safe.
     */
    private int onlineCount = 0;

    /**
     * concurrent The thread-safe Set of the package is used to store MyWebSocket objects corresponding to each client.
     */
    public CopyOnWriteArraySet<BaseWebSocket> webSocketSet = new CopyOnWriteArraySet<BaseWebSocket>();

    /**
     * Connecting session with a client needs to be used to send data to the client
     */
    public Session session;

    private Logger log = LoggerFactory.getLogger("BaseWebSocket");


    /**
     * Connection Establishment Successful Call Method
     * @param session
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("sid") String sid) throws IOException {
        this.session = session;
        //Join set
        webSocketSet.add(this);
        //On-line number plus 1
        addOnlineCount();
        log.debug("New connections to join! The current number of people online is" + getOnlineCount());
        //Send information
        MultiMap multiMap = new MultiMap();
        if (null!=session.getQueryString()&&!"".equals(session.getQueryString())) {
            UrlEncoded.decodeTo(session.getQueryString(), multiMap, "UTF-8");
        }
        sendInfo(defaultMessage(multiMap));
    }

    /**
     * Connection Close Call Method
     */
    @OnClose
    public void onClose() {
        //Delete from set
        webSocketSet.remove(this);
        //On-line number minus 1
        subOnlineCount();
        log.info("A connection is closed! The current number of people online is" + getOnlineCount());
    }

    /**
     * Method called after receiving client message
     * @param message Message sent by client
     * @param session cache
     * @throws IOException
     */
    @OnMessage
    public void onMessage(String message, Session session) throws IOException {
        this.session = session;
        try {
            Map paraMap = (Map) JSONObject.parse(message);
            handlerMessage(paraMap);
        } catch (JSONException e) {
            MultiMap multiMap = new MultiMap();
            UrlEncoded.decodeTo(message, multiMap, "UTF-8");
            handlerMessage(multiMap);
            //Throw new Business Exception ("Json");
        }
    }

    /**
     * Processing message acceptance
     * @param paraMap Accept parameters of map type
     */
    public void handlerMessage(Map paraMap) {
        try {
            sendInfo(defaultMessage(paraMap));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public Object defaultMessage(Map<String, Object> paraMap) {
        Object obj = new Object();
        try {
            obj = defauleMessage(paraMap);
        } catch (BusinessException e) {
            return formatReturnAppData(e.getMessage());
        }
        return obj;
    }
    /**
     * Default send data
     * @param paraMap Parameters Transferred during Connection
     * @return
     */
    public abstract Object defauleMessage(Map<String, Object> paraMap);

    public static boolean isJson(String content) {
        try {
            JSONObject.parse(content);
            return true;
        } catch (Exception e) {
            return false;
        }
    }
    /**
     * Called when an error occurs
     @OnError
     **/
    public void onError(Session session, Throwable error) {
        log.error("onMessage Method anomaly"+error.toString());
        error.printStackTrace();
    }


    /**
     * Attention should be paid to locking synchronized messages to avoid blocking error reporting
     * Note the difference between session.getBasicRemote() and session.getAsyncRemote().
     * @param message
     * @throws IOException
     */
    public synchronized void sendMessage(Object message) throws IOException {
//         this.session.getBasicRemote().sendText(message);
        this.session.getAsyncRemote().sendText(JSONObject.toJSONString(message));
    }


    /**
     * Group sending custom messages
     * */
    public void sendInfo(Object message) throws IOException {
        for (BaseWebSocket item : webSocketSet) {
            try {
                item.sendMessage(message);
            } catch (IOException e) {
                continue;
            }
        }
    }

    public  synchronized int getOnlineCount() {
        return onlineCount;
    }

    public  synchronized void addOnlineCount() {
        onlineCount++;
    }

    public  synchronized void subOnlineCount() {
        onlineCount--;
    }
}

  • Then when we add a new websocket, we just need to inherit the abstract websocket class and reproduce the defauleMessage method inside. The following annotations need to be added to the class
@ServerEndpoint(value = "/accident/getAccident")
@Component
public class AccidentGetAccident extends BaseWebSocket {

    AccidentController accidentController;


    @Override
    public Object defauleMessage(Map<String, Object> paraMap) {
        accidentController = ContextUtil.getApplicationContext().getBean(AccidentController.class);
        return accidentController.getAccident(paraMap);
    }
}

Client Connection

  • At the front end, you just need to construct the WebSocket object, which needs to pass a parameter, the connection address.
ws = new WebSocket(wsUrl);

  • Then we can rewrite some of the events in ws
ws.onclose = function () {
    console.log('Link closure');
};
ws.onerror = function() {
    console.log('Something unusual has happened.');
};
ws.onopen = function () {
    console.log('make new connection');
};
ws.onmessage = function (event) {
    console.log("Received feedback from the server.");
}

  • But we have to consider a situation that our server is down because of some factors, when the client captures the onclose event, the connection will end, but the server may be repaired in a short time. At this time, we need the client to refresh to reconnect. websocket is normally used in large screen, sometimes it is not very convenient to refresh, so we need our client to have a reconnection mechanism at this time.
var lockReconnect = false;//Avoid duplicate connections
    var wsUrl = "ws://127.0.0.1:8088/accident/getAccident?entId=zhonghuaxingzhong";
    var ws;
    var tt;
    function createWebSocket() {
        try {
            ws = new WebSocket(wsUrl);
            init();
        } catch(e) {
            console.log(e+'catch');
            reconnect(wsUrl);
        }
    }
    function init() {
        ws.onclose = function () {
            console.log('Link closure');
            reconnect(wsUrl);
        };
        ws.onerror = function() {
            console.log('Something unusual has happened.');
            reconnect(wsUrl);
        };
        ws.onopen = function () {
            //Heart beat detection reset
            heartCheck.start();
        };
        ws.onmessage = function (event) {
            setMessageInnerHTML(event.data);
            //Getting any message indicates that the current connection is normal.
            console.log('Receive a message');
            heartCheck.start();
        }
    }
    function reconnect(url) {
        if(lockReconnect) {
            return;
        };
        lockReconnect = true;
        //No connection will always be reconnected, set the delay to avoid too many requests
        tt && clearTimeout(tt);
        tt = setTimeout(function () {
            createWebSocket(url);
            lockReconnect = false;
        }, 4000);
    }
    //runtastic Heart Rate PRO
    var heartCheck = {
        timeout: 3000,
        timeoutObj: null,
        serverTimeoutObj: null,
        start: function(){

        }
    }
    //Display the message on the web page
    function setMessageInnerHTML(innerHTML) {
        document.getElementById('message').innerHTML += innerHTML + '<br/>';
    }
    createWebSocket(wsUrl);

Join the team

Join the team

Wechat Public Number

theme

Posted by McJepp on Tue, 06 Aug 2019 02:24:09 -0700