SpringBoot + WebSocket implements simple message push

WebSocket is a network protocol for duplex communication. The so-called duplex means that the browser can send messages to the server, and the server can also send messages to the browser. Then, with the HTTP protocol, why use it? Suppose we need the server to push messages to the client regularly every day, then the HTTP protocol cannot actively send information to the client, but WebSocket can achieve this function, it will automatically monitor the browser and make an automatic reply.

Features of WebSocket:
(1) Built on the TCP protocol, the server-side implementation is relatively easy.

(2) It has good compatibility with the HTTP protocol. The default ports are also 80 and 443, and the HTTP protocol is used in the handshake phase, so it is not easy to shield during the handshake, and it can pass through various HTTP proxy servers.

(3) The data format is relatively lightweight, the performance overhead is small, and the communication is efficient.

(4) Text can be sent, and binary data can also be sent.

(5) There is no same-origin restriction, and the client can communicate with any server.

(6) The protocol identifier is ws (or wss if encrypted) and the server URL is the URL.

Let's use a simple case to better understand the principle and use of WebSocket:
In the following case, the browser will send a message 'hello' to the server, and the server will reply to the server after receiving the message and print it on the console.

First create a maven project and import the corresponding dependencies:

<!-- maven项目packaging改为war类型时,必须要加这个插件 -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.3</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- 测试springboot的依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- 测试时加入这两个依赖,当修改代码的时候就不用每次修改后重启服务器了 -->
        <!-- https://mvnrepository.com/artifact/org.springframework/springloaded -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>springloaded</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-devtools -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
        <!-- springboot 集成jsp必须要借助这两个依赖 -->
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-tomcat -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-jasper -->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
        </dependency>
        <!-- WebSocket相关依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
    </dependencies>

To implement WebSocket, you need to implement the WebSocketHandler interface. The source code is as follows:

public interface WebSocketHandler {
    void afterConnectionEstablished(WebSocketSession session) throws Exception;
    void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception;
    void handleTransportError(WebSocketSession session, Throwable exception) throws Exception;
    void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception;
    boolean supportsPartialMessages();

}

If you need to receive binary messages, you can also extend the abstract class AbstractWebSocketHandler, which implements the WebSocketHandler interface. It adds the following additional methods:

public abstract class AbstractWebSocketHandler implements WebSocketHandler {

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
    }

    @Override
    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
        if (message instanceof TextMessage) {
            handleTextMessage(session, (TextMessage) message);
        }
        else if (message instanceof BinaryMessage) {
            handleBinaryMessage(session, (BinaryMessage) message);
        }
        else if (message instanceof PongMessage) {
            handlePongMessage(session, (PongMessage) message);
        }
        else {
            throw new IllegalStateException("Unexpected WebSocket message type: " + message);
        }
    }

    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
    }

    protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) throws Exception {
    }

    protected void handlePongMessage(WebSocketSession session, PongMessage message) throws Exception {
    }

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

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

    @Override
    public boolean supportsPartialMessages() {
        return false;
    }

}

The handleTextMessage method handles text messages, and handleBinaryMessage receives binary messages for processing. Maybe sometimes it is not necessary to receive binary messages. That needs to extend the TextWebSocketHandler class, which inherits from the AbstractWebSocketHandler class. The handleBinaryMessage method in this class will close the communication when it receives a binary message.

public class TextWebSocketHandler extends AbstractWebSocketHandler {

    @Override
    protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) {
        try {
            session.close(CloseStatus.NOT_ACCEPTABLE.withReason("Binary messages not supported"));
        }
        catch (IOException ex) {
            // ignore
        }
    }

}

Below we extend the AbstractWebSocketHandler class to implement the use of WebSocket:

package cn.shinelon.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.AbstractWebSocketHandler;
@Component
public class SocketHandler extends AbstractWebSocketHandler{
    public Logger log=LoggerFactory.getLogger(getClass());
    /**
     * 建立连接之后
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        log.info("Connection established");
    }
    /**
     * 处理文本消息
     */
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
            log.info("Received message: "+message.getPayload());
            //模拟延时
            Thread.sleep(2000);
            //发送文本消息
            session.sendMessage(new TextMessage("Server"));
    }
    /**
     * 连接关闭之后
     */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        log.info("Connection closed Status:"+status);
    }
}

In addition, the WebSocket needs to be registered and injected into the Spring container:

package cn.shinelon.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer{
    @Bean
    public SocketHandler socketHander(){
        return new SocketHandler();
    }
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(socketHander(), "/hello").withSockJS();
    }
}

The following is the jsp page code, mainly the JS code inside:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src="http://cdn.jsdelivr.net/sockjs/0.3.4/sockjs.min.js"></script> 
<script type="text/javascript">
//var url='ws://'+window.location.host+'/webSocket/hello';
var url='hello';
//var socket=new WebSocket(url);
var socket=new SockJS(url);
socket.onopen=function(){
    console.log('Opening');
    sayHello();
};
//处理消息
socket.onmessage=function(e){
    console.log('Received message:',e.data);
    setTimeout(function(){sayHello()},2000);
};
//处理关闭事件
socket.onclose=function(){
    console.log('Closing');
};
function sayHello(){
    console.log('Sending hello');
    //发送消息
    socket.send('Hello');
}
</script>
</head>
<body>
    this is springboot
</body>
</html>

Explain the above code: Most of the current browsers support WebSocket, such as Chrom, FireFox and other mainstream browsers, but some older browsers do not support it. If your browser supports WebSocket, you can use new WebSocket( url) to create, if not supported, you need to use new SockJS(url), which uses the HTTP protocol, but needs to import the library ( http://cdn.jsdelivr.net/sockjs/0.3.4/sockjs.min. js ). That is, if your browser does not support WebSocket, you need to:

var url='ws://'+window.location.host+'/webSocket/hello';
var socket=new WebSocket(url);

Change it to:

var url='hello';
var socket=new SockJS(url);

The following is the configuration of SpringBoot's resource file:
application.properties:

spring.mvc.view.prefix=WEB-INF/jsp/
spring.mvc.view.suffix=.jsp

Finally, write a simple control class to start the project and see the effect:

@SpringBootApplication
@Controller
@ComponentScan("cn.shinelon")
public class IndexController {

    @RequestMapping("/ws")
    public String ws() {
        return "index";
    }
}   

Then enter localhost:8080/ws in the browser, and you will see that the browser and the server keep sending messages:
write picture description here

write picture description here

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324650166&siteId=291194637