Development of real-time communication applications: Vue.js, Spring Boot and WebSocket integration practice

Table of contents

1. What is webSocket 

2. What can webSocket be used for?

3. webSocket protocol

4. Server side

5. Client

6. Test communication


1. What is webSocket 

WebSocket is a protocol for full-duplex communication over a single TCP connection . WebSocket makes the data exchange between the client and the server easier, allowing the server to actively push data to the client . In the WebSocket API, the browser and the server only need to complete a handshake, and a persistent connection can be created directly between the two, and two-way data transmission can be performed.

2. What can webSocket be used for?

Using the characteristics of two-way data transmission can be used to complete many functions, without front-end polling, wasting resources. For example: 

  • Real-time chat application : WebSocket can implement real-time chat function, enabling users to send and receive messages instantly and realize fast and real-time communication.

  • Real-time data update : For applications that need to update data in real time, such as stock quotes, weather forecasts, etc., WebSocket can provide a real-time push mechanism, so that data can be updated in real time and displayed to users in a timely manner.

  • Multiplayer online games : WebSocket provides two-way communication capabilities, suitable for real-time battle scenarios of multiplayer online games, such as real-time card games, board games, etc.

  • Collaboration tools : With WebSocket, real-time collaboration tools can be implemented, such as team task management tools, collaborative editing tools, etc., enabling team members to update and synchronize work content in real time.

  • Real-time map traffic monitoring : WebSocket can be used in a real-time map traffic monitoring system, enabling users to obtain information such as road conditions and traffic congestion in real time.

  • Online customer service and customer support : Through WebSocket, customer service personnel can communicate with customers in real time, providing timely answers to questions and support services.

  • Real-time voting and survey : Using WebSocket, you can implement a real-time voting and survey system, display voting results in real time, and receive and count user votes. etc......

3. webSocket protocol

This protocol has two parts: handshake and data transfer. The handshake is based on the http protocol. 

  • The handshake from the client looks like this:
GET ws://localhost/chat HTTP/1.1
Host: localhost
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key:dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Protocol: chat,superchat
Sec-WebSocket-Version: 13
  • The handshake from the server looks like this:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept:s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat

4. Server side

  • pom.xml dependencies
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
  • WebSocketConfig configuration class (config package directory)
package com.jmh.demo03.websocket.config;

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

@Configuration
public class WebSocketConfig {
    /**
     * 	注入ServerEndpointExporter,
     * 	这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }

}
  •  WebSocket operation class (config package directory)
package com.jmh.demo03.websocket.config;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;

import org.springframework.stereotype.Component;

import lombok.extern.slf4j.Slf4j;

@Component
@Slf4j
//接口路径 ws://localhost:8087/webSocket/userId;
@ServerEndpoint("/websocket/{userId}")

public class WebSocket {

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

    /**
     * 用户ID
     */
    private String userId;

    //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
    //虽然@Component默认是单例模式的,但springboot还是会为每个websocket连接初始化一个bean,所以可以用一个静态set保存起来。
    //  注:底下WebSocket是当前类名
    private static CopyOnWriteArraySet<WebSocket> webSockets =new CopyOnWriteArraySet<>();

    /**
     * 用来存在线连接用户信息
     */
    private static ConcurrentHashMap<String,Session> sessionPool = new ConcurrentHashMap<String,Session>();


    /**
     * 链接成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam(value="userId")String userId) {
        try {
            this.session = session;
            this.userId = userId;
            webSockets.add(this);
            sessionPool.put(userId, session);
            log.info("【websocket消息】用户{}连接成功,在线人数为{}",userId,webSockets.size());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 链接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        try {
            webSockets.remove(this);
            sessionPool.remove(this.userId);
            log.info("【websocket消息】用户{}连接断开,在线人数为{}",userId,webSockets.size());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 收到客户端消息后调用的方法
     * @param message 消息
     */
    @OnMessage
    public void onMessage(String message) {
        log.info("【websocket消息】收到客户端用户{},消息:{}",userId,message);
    }

    /**
     * 发送错误时的处理
     * @param session 会话
     * @param error 错误
     */
    @OnError
    public void onError(Session session, Throwable error) {

        log.error("用户错误,原因:"+error.getMessage());
        error.printStackTrace();
    }

    /**
     * 此为广播消息
     * @param message 消息内容
     */
    public void sendAllMessage(String message) {
        log.info("【websocket消息】广播消息:"+message);
        for(WebSocket webSocket : webSockets) {
            try {
                if(webSocket.session.isOpen()) {
                    webSocket.session.getAsyncRemote().sendText(message);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 此为单点消息
     * @param userId 用户编号
     * @param message 消息内容
     */
    public void sendOneMessage(String userId, String message) {
        Session session = sessionPool.get(userId);
        if (session != null&&session.isOpen()) {
            try {
                log.info("【websocket消息】 单点消息:"+message);
                session.getAsyncRemote().sendText(message);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 此为单点消息(多人)
     * @param userIds 用户编号组
     * @param message 消息内容
     */
    public void sendMoreMessage(String[] userIds, String message) {
        for(String userId:userIds) {
            Session session = sessionPool.get(userId);
            if (session != null&&session.isOpen()) {
                try {
                    log.info("【websocket消息】 单点消息:"+message);
                    session.getAsyncRemote().sendText(message);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

    }

}
  • SendMessageController calls the instance class (controller package directory)
package com.jmh.demo03.websocket.controller;

import com.alibaba.fastjson2.JSONObject;
import com.jmh.demo03.websocket.config.WebSocket;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * @author 蒋明辉
 * @data 2023/8/7 14:43
 */
@RestController
@RequestMapping("/ws")
public class SendMessageController {

    @Autowired
    private WebSocket webSocket;

    /**
     * 广播消息
     */
    @PostMapping("/sendAllMessage")
    public void sendAllMessage(){
        //发送内容
        JSONObject jsonObject=new JSONObject();
        String message="【今日话题】:早睡早起,多喝开水,要爱自己多一点~";
        jsonObject.put("message",message);
        //全体发送
        webSocket.sendAllMessage(jsonObject.toString());
    }

    /**
     * 单个用户发送
     */
    @PostMapping("/sendOneMessage")
    public void sendOneMessage(){
        //发送内容
        JSONObject jsonObject=new JSONObject();
        String message="【今日话题】:早睡早起,多喝开水,要爱自己多一点~";
        jsonObject.put("abc",message);
        //单个用户发送 (userId为用户id)
        webSocket.sendOneMessage("1", jsonObject.toString());
    }

    /**
     * 多个用户发送
     */
    @PostMapping("/sendMoreMessage")
    public void sendMoreMessage(){
        //发送内容
        JSONObject jsonObject=new JSONObject();
        String message="【今日话题】:早睡早起,多喝开水,要爱自己多一点~";
        jsonObject.put("message",message);
        //多个用户发送 (userIds为多个用户id,逗号‘,’分隔)
        String str="1,2,3";
        String[] split = str.split(",");
        webSocket.sendMoreMessage(split, jsonObject.toString());
    }




}

5. Client

  • vue.js code 
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<title></title>
		<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
		<script src="https://cdn.bootcss.com/vue/2.6.10/vue.js"></script>
		<script src="https://cdn.bootcdn.net/ajax/libs/axios/1.3.4/axios.js"></script>
	</head>
	<body>
		<div id="demo">
			<h1>Web Socket 正式发版</h1>
			
			<hr/>
			
			<button @click="sendKh()">发送消息(客户端)</button>
			<button @click="sendFwq()">发送消息(服务器端)</button>
			
		</div>
	</body>
	
	<script>
		new Vue({
			el: '#demo',
			data() {
				return {
					
				}
			},
			mounted() { 
				  //初始化websocket
				  this.initWebSocket()
			},
			destroyed: function () { // 离开页面生命周期函数
				  this.websocketclose();
			},
			methods: {
				initWebSocket: function () { // 建立连接
					// WebSocket与普通的请求所用协议有所不同,ws等同于http,wss等同于https
					this.websock = new WebSocket("ws://127.0.0.1:8080/websocket/1");
					this.websock.onopen = this.websocketonopen;
					//this.websock.send = this.websocketsend;
					this.websock.onerror = this.websocketonerror;
					this.websock.onmessage = this.websocketonmessage;
					this.websock.onclose = this.websocketclose;
					
				  },
				  // 连接成功后调用
				  websocketonopen: function () {
					 console.log("WebSocket连接成功");
				  },
				  // 发生错误时调用
				  websocketonerror: function (e) {
					console.log("WebSocket连接发生错误");
				  },
				  // 给后端发消息时调用
				  websocketsend: function (e) {
					console.log("WebSocket连接发生错误");
				  },
				  // 接收后端消息(服务器端发送)
				  // vue 客户端根据返回的cmd类型处理不同的业务响应
				  websocketonmessage: function (e) {
					console.info(e)
					var data = eval("(" + e.data + ")"); 
					console.info(data)
				  },
				  // 关闭连接时调用
				  websocketclose: function (e) {
					console.log("connection closed (" + e.code + ")");
				  },
				   //向服务器发送消息(客户端发送)
				  sendKh(){
					  let data={
						  message: '小明你好!我想和你成为朋友~',
						  sendData: new Date()
					  }
					  this.websock.send("尊嘟假嘟")
				  },
				  //向客户端发送消息(服务器端发送)
				  sendFwq(){
					  axios.post('http://localhost:8080/ws/sendOneMessage').then((resp)=>{
					  	console.info(resp)
					  })
				  }
			}
		})
	</script>
</html>

6. Test communication

1. Connect to the communication (open the front-end and back-end access services to contact the communication)

2. The client sends a message to the server

  •  The client sends a message to the server

  • The message received by the server 

3. The server sends a message to the client

  • The server sends a message to the client

  •  The message received by the client

  •  View server-side messages

Guess you like

Origin blog.csdn.net/m0_63300795/article/details/132159540