SpringBoot+Vue integrates WebSocket to achieve front-end and back-end message push

Scenes

WebSocket

The HTTP protocol is a stateless, connectionless, and one-way application layer protocol. It uses a request/response model. The communication request can only be initiated by the client, and the server responds to the request.

This communication model has a drawback: the HTTP protocol cannot enable the server to actively initiate messages to the client.

The characteristic of this one-way request is destined to be very troublesome for the client to know if the server has continuous state changes. Most web applications will implement long polling through frequent asynchronous JavaScript and XML (AJAX) requests. Polling is inefficient and wastes resources (because the connection must be kept connected or the HTTP connection is always open).

This is how WebSocket was invented. The WebSocket connection allows full-duplex communication between the client and the server so that either party can push data to the other end through the established connection. WebSocket only needs to establish a connection once, and it can stay connected all the time. Compared with the continuous establishment of the connection in the polling mode, the efficiency is obviously greatly improved.

If you follow the front-end and back-end separated version to teach you to set up the environment locally and run the project:

https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/108465662

After setting up the front and back end projects above. Perform the WebSocket integration of SpringBoot and Vue in the background.

Note:

Blog:
https://blog.csdn.net/badao_liumang_qizhi
Follow the public
account Domineering
programmers Get programming-related e-books, tutorial pushes and free downloads.

achieve

SpringBoot integration

First introduce dependencies in the pom file

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>

Then create a new configuration class for WebSocket to enable WebSocket support

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@Configuration
public class WebSocketConfig {

    @Bean
    public ServerEndpointExporter serverEndpointExporter(){
        return  new ServerEndpointExporter();
    }
}

Then create a new client entity class WebSocketClient to store the connected Session and Uri

import javax.websocket.Session;

public class WebSocketClient {
    // 与某个客户端的连接会话,需要通过它来给客户端发送数据
    private Session session;

    //连接的uri
    private String uri;

    public Session getSession() {
        return session;
    }

    public void setSession(Session session) {
        this.session = session;
    }

    public String getUri() {
        return uri;
    }

    public void setUri(String uri) {
        this.uri = uri;
    }
}

Then create a new WebSocketService to create and process connections

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;

@ServerEndpoint(value = "/websocket/{userName}")
@Component
public class WebSocketService {

    private static final Logger log = LoggerFactory.getLogger(WebSocketService.class);

    //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
    private static int onlineCount = 0;
    //concurrent包的线程安全Set,用来存放每个客户端对应的WebSocketServer对象。
    private static ConcurrentHashMap<String, WebSocketClient> webSocketMap = new ConcurrentHashMap<>();


    /**与某个客户端的连接会话,需要通过它来给客户端发送数据*/
    private Session session;
    /**接收userName*/
    private String userName="";
    /**
     * 连接建立成功调用的方法*/
    @OnOpen
    public void onOpen(Session session, @PathParam("userName") String userName) {
        if(!webSocketMap.containsKey(userName))
        {
            addOnlineCount(); // 在线数 +1
        }
        this.session = session;
        this.userName= userName;
        WebSocketClient client = new WebSocketClient();
        client.setSession(session);
        client.setUri(session.getRequestURI().toString());
        webSocketMap.put(userName, client);

        log.info("----------------------------------------------------------------------------");
        log.info("用户连接:"+userName+",当前在线人数为:" + getOnlineCount());
        try {
            sendMessage("来自后台的反馈:连接成功");
        } catch (IOException e) {
            log.error("用户:"+userName+",网络异常!!!!!!");
        }
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        if(webSocketMap.containsKey(userName)){
            webSocketMap.remove(userName);
            if(webSocketMap.size()>0)
            {
                //从set中删除
                subOnlineCount();
            }
        }
        log.info("----------------------------------------------------------------------------");
        log.info(userName+"用户退出,当前在线人数为:" + getOnlineCount());
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息*/
    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("收到用户消息:"+userName+",报文:"+message);
        //可以群发消息
        //消息保存到数据库、redis
        if(StringUtils.isNotBlank(message)){

        }
    }

    /**
     *
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("用户错误:"+this.userName+",原因:"+error.getMessage());
        error.printStackTrace();
    }

    /**
     * 连接服务器成功后主动推送
     */
    public void sendMessage(String message) throws IOException {
        synchronized (session){
            this.session.getBasicRemote().sendText(message);
        }
    }

    /**
     * 向指定客户端发送消息
     * @param userName
     * @param message
     */
    public static void sendMessage(String userName,String message){
        try {
            WebSocketClient webSocketClient = webSocketMap.get(userName);
            if(webSocketClient!=null){
                webSocketClient.getSession().getBasicRemote().sendText(message);
            }
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException(e.getMessage());
        }
    }
   

    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        WebSocketService.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        WebSocketService.onlineCount--;
    }

    public static void setOnlineCount(int onlineCount) {
        WebSocketService.onlineCount = onlineCount;
    }


    public static ConcurrentHashMap<String, WebSocketClient> getWebSocketMap() {
        return webSocketMap;
    }

    public static void setWebSocketMap(ConcurrentHashMap<String, WebSocketClient> webSocketMap) {
        WebSocketService.webSocketMap = webSocketMap;
    }

    public Session getSession() {
        return session;
    }

    public void setSession(Session session) {
        this.session = session;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

}

Note that the WebSocketClient referenced here is the entity class created above to store connection-related information.

然后@ServerEndpoint(value = "/websocket/{userName}")

Here is the address of the WebSocket connection, the following {userName} is used to receive the parameters passed by the front end, used as a different identifier, and then do different processing

Then the following OnOpen annotated method is the method to be called after the connection is established successfully, here is the counting process of the number of users online, you can according to your own

The business needs to be dealt with.

Then OnClose annotates the method called by the connection relationship.

Then OnMessage annotates the callback method when the client sends a message, which processes the data according to their needs

The sendMessage method uses the current session to push a feedback message to the client when the client connects to the server

Then send a message to the specified client using the sendMessage method, which passes the user’s unique identifier and message content

Create a new Controller interface to test the push of messages

import com.ruoyi.web.websocket.WebSocketService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
@RequestMapping("/websocket")
public class WebSocketController {


    @GetMapping("/pushone")
    public void pushone()
    {
        WebSocketService.sendMessage("badao","公众号:霸道的程序猿");
    }
}

If it is a normal SpringBoot project, the backend is all right. Then because the framework built by Ruoyi above is used, the url of ws and the url of the interface need to be released for authorization authentication

 

Vue project integration

Create a new WebSocket component, which is placed in the componets directory

 

Then declare some variables

        data() {
            return {
                // ws是否启动
                wsIsRun: false,
                // 定义ws对象
                webSocket: null,
                // ws请求链接(类似于ws后台地址)
                ws: '',
                // ws定时器
                wsTimer: null,
            }
        },

Execute the method of initializing the websocket connection in the mounted function

        async mounted() {
            this.wsIsRun = true
            this.wsInit()
        },

In the implementation of the initialization method

           const wsuri = 'ws://你的后台ip:7777/websocket/badao'
                this.ws = wsuri
                if (!this.wsIsRun) return
                // 销毁ws
                this.wsDestroy()
                // 初始化ws
                this.webSocket = new WebSocket(this.ws)
                // ws连接建立时触发
                this.webSocket.addEventListener('open', this.wsOpenHanler)
                // ws服务端给客户端推送消息
                this.webSocket.addEventListener('message', this.wsMessageHanler)
                // ws通信发生错误时触发
                this.webSocket.addEventListener('error', this.wsErrorHanler)
                // ws关闭时触发
                this.webSocket.addEventListener('close', this.wsCloseHanler)

                // 检查ws连接状态,readyState值为0表示尚未连接,1表示建立连接,2正在关闭连接,3已经关闭或无法打开
                clearInterval(this.wsTimer)
                this.wsTimer = setInterval(() => {
                    if (this.webSocket.readyState === 1) {
                        clearInterval(this.wsTimer)
                    } else {
                        console.log('ws建立连接失败')
                        this.wsInit()
                    }
                }, 3000)
            },

Make a websocket connection and pass badao parameters, and set the corresponding callback processing method.

Finally, a 3-second timer is set to periodically check the connection status of the websocket.

This component also adds a button and sets its click event to push messages to the server

            sendDataToServer() {
                if (this.webSocket.readyState === 1) {
                    this.webSocket.send('来自前端的数据')
                } else {
                    throw Error('服务未连接')
                }
            },

Complete WebSocket component code

<template>
  <el-button @click="sendDataToServer" >给后台发送消息</el-button>
</template>

<script>
    export default {
        name: "WebSocket",
        data() {
            return {
                // ws是否启动
                wsIsRun: false,
                // 定义ws对象
                webSocket: null,
                // ws请求链接(类似于ws后台地址)
                ws: '',
                // ws定时器
                wsTimer: null,
            }
        },
        async mounted() {
            this.wsIsRun = true
            this.wsInit()
        },
        methods: {
            sendDataToServer() {
                if (this.webSocket.readyState === 1) {
                    this.webSocket.send('来自前端的数据')
                } else {
                    throw Error('服务未连接')
                }
            },
            /**
             * 初始化ws
             */
            wsInit() {
                const wsuri = 'ws://10.229.36.158:7777/websocket/badao'
                this.ws = wsuri
                if (!this.wsIsRun) return
                // 销毁ws
                this.wsDestroy()
                // 初始化ws
                this.webSocket = new WebSocket(this.ws)
                // ws连接建立时触发
                this.webSocket.addEventListener('open', this.wsOpenHanler)
                // ws服务端给客户端推送消息
                this.webSocket.addEventListener('message', this.wsMessageHanler)
                // ws通信发生错误时触发
                this.webSocket.addEventListener('error', this.wsErrorHanler)
                // ws关闭时触发
                this.webSocket.addEventListener('close', this.wsCloseHanler)

                // 检查ws连接状态,readyState值为0表示尚未连接,1表示建立连接,2正在关闭连接,3已经关闭或无法打开
                clearInterval(this.wsTimer)
                this.wsTimer = setInterval(() => {
                    if (this.webSocket.readyState === 1) {
                        clearInterval(this.wsTimer)
                    } else {
                        console.log('ws建立连接失败')
                        this.wsInit()
                    }
                }, 3000)
            },
            wsOpenHanler(event) {
                console.log('ws建立连接成功')
            },
            wsMessageHanler(e) {
                console.log('wsMessageHanler')
                console.log(e)
                //const redata = JSON.parse(e.data)
                //console.log(redata)
            },
            /**
             * ws通信发生错误
             */
            wsErrorHanler(event) {
                console.log(event, '通信发生错误')
                this.wsInit()
            },
            /**
             * ws关闭
             */
            wsCloseHanler(event) {
                console.log(event, 'ws关闭')
                this.wsInit()
            },
            /**
             * 销毁ws
             */
            wsDestroy() {
                if (this.webSocket !== null) {
                    this.webSocket.removeEventListener('open', this.wsOpenHanler)
                    this.webSocket.removeEventListener('message', this.wsMessageHanler)
                    this.webSocket.removeEventListener('error', this.wsErrorHanler)
                    this.webSocket.removeEventListener('close', this.wsCloseHanler)
                    this.webSocket.close()
                    this.webSocket = null
                    clearInterval(this.wsTimer)
                }
            },
        }
    }
</script>

<style scoped>

</style>

Then reference the component in the homepage

<template>
  <div class="app-container home">
    <el-row :gutter="20">
      websocket推送
      <WebSocket></WebSocket>
    </el-row>
    <el-divider />
  </div>
</template>
import WebSocket from '@/components/WebSocket/WebSocket'
export default {
  name: "index",
   components: {
     WebSocket
  },

effect

First run the background SpringBoot service, then run the front-end project and log in to the home page, open the console output

 

You can see that the websocket connection is successfully established, and there is output in the background

 

Then the background pushes the message to the front end and calls the interface for sending the message in the background

Then you can receive the push from the background at the front end

 

Then click the button on the front end to send a message to the background

The sent message is received in the callback method in the background

 

Guess you like

Origin blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/114392573