Integrating websocket for real-time communication (ruoyi usage notes)

1. A brief introduction to WebSocket

Websocket is a full-duplex communication protocol based on TCP protocol, which enables real-time two-way communication between client and server. Compared with the traditional HTTP protocol, which can only send requests through the client and wait for the response from the server, WebSocket allows the client and the server to send messages to each other at any time. This real-time communication method is very suitable for applications that need to update data in real time Scenarios, such as chat rooms, online games, stock quotes, etc.

The operation process of WebSocket is as follows:

The client initiates a WebSocket handshake request to the server;
the server returns a confirmation message to the client to complete the handshake;
after the handshake is successful, the client and server can communicate;
both parties can send messages to each other at any time, or close the connection.

The benefits of WebSockets include:

Real-time : WebSocket provides two-way communication capability, which can realize the function of updating data in real time;
Reliability : WebSocket is based on TCP protocol, which can ensure the reliability of message transmission;
High performance : WebSocket has low overhead, high communication efficiency, and will not frequently Perform operations such as connection and disconnection to reduce network latency;
cross-domain support : WebSocket supports cross-domain communication, and can establish connections between different domains.

In front-end development, JavaScript WebSocket API can be used to operate WebSocket. Common libraries include Socket.io and WebSocket-Node. In back-end development, the implementation of WebSocket can use Node.js, Java, Python and other languages ​​and frameworks.

Of course, some security issues also need to be considered when using WebSocket, such as avoiding cross-site scripting attacks (XSS), preventing malicious requests, and so on.

Just click on the URL ;
insert image description here
link: https://pan.baidu.com/s/13JVC9jm-Dp9PfHdDDylLCQ Extraction code: y9jt

2. Detailed code

(Everyone can go to Ruoyi to download and record it myself)

2.1WebSocketConfig

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

/**
 * websocket 配置
 * 
 * @author ruoyi
 */
@Configuration
public class WebSocketConfig
{
    
    
    @Bean
    public ServerEndpointExporter serverEndpointExporter()
    {
    
    
        return new ServerEndpointExporter();
    }
}

2.2 SemaphoreUtils

import java.util.concurrent.Semaphore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 信号量相关处理
 * 
 * @author ruoyi
 */
public class SemaphoreUtils
{
    
    

    /**
     * SemaphoreUtils 日志控制器
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(SemaphoreUtils.class);

    /**
     * 获取信号量
     * 
     * @param semaphore
     * @return
     */
    public static boolean tryAcquire(Semaphore semaphore)
    {
    
    
        boolean flag = false;

        try
        {
    
    
            flag = semaphore.tryAcquire();
        }
        catch (Exception e)
        {
    
    
            LOGGER.error("获取信号量异常", e);
        }

        return flag;
    }

    /**
     * 释放信号量
     * 
     * @param semaphore
     */
    public static void release(Semaphore semaphore)
    {
    
    

        try
        {
    
    
            semaphore.release();
        }
        catch (Exception e)
        {
    
    
            LOGGER.error("释放信号量异常", e);
        }
    }
}

2.3 WebSocketServer



import java.util.concurrent.Semaphore;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

import com.lxh.demo.util.SemaphoreUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

/**
 * websocket 消息处理
 * 
 * @author ruoyi
 */
@Component
@ServerEndpoint("/websocket/message")
public class WebSocketServer
{
    
    
    /**
     * WebSocketServer 日志控制器
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketServer.class);

    /**
     * 默认最多允许同时在线人数100
     */
    public static int socketMaxOnlineCount = 100;

    private static Semaphore socketSemaphore = new Semaphore(socketMaxOnlineCount);

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session) throws Exception
    {
    
    
        boolean semaphoreFlag = false;
        // 尝试获取信号量
        semaphoreFlag = SemaphoreUtils.tryAcquire(socketSemaphore);
        if (!semaphoreFlag)
        {
    
    
            // 未获取到信号量
            LOGGER.error("\n 当前在线人数超过限制数- {}", socketMaxOnlineCount);
            WebSocketUsers.sendMessageToUserByText(session, "当前在线人数超过限制数:" + socketMaxOnlineCount);
            session.close();
        }
        else
        {
    
    
            // 添加用户
            WebSocketUsers.put(session.getId(), session);
            LOGGER.info("\n 建立连接 - {}", session);
            LOGGER.info("\n 当前人数 - {}", WebSocketUsers.getUsers().size());
            WebSocketUsers.sendMessageToUserByText(session, "连接成功");
        }
    }

    /**
     * 连接关闭时处理
     */
    @OnClose
    public void onClose(Session session)
    {
    
    
        LOGGER.info("\n 关闭连接 - {}", session);
        // 移除用户
        WebSocketUsers.remove(session.getId());
        // 获取到信号量则需释放
        SemaphoreUtils.release(socketSemaphore);
    }

    /**
     * 抛出异常时处理
     */
    @OnError
    public void onError(Session session, Throwable exception) throws Exception
    {
    
    
        if (session.isOpen())
        {
    
    
            // 关闭连接
            session.close();
        }
        String sessionId = session.getId();
        LOGGER.info("\n 连接异常 - {}", sessionId);
        LOGGER.info("\n 异常信息 - {}", exception);
        // 移出用户
        WebSocketUsers.remove(sessionId);
        // 获取到信号量则需释放
        SemaphoreUtils.release(socketSemaphore);
    }

    /**
     * 服务器接收到客户端消息时调用的方法
     */
    @OnMessage
    public void onMessage(String message, Session session)
    {
    
    
        String msg = message.replace("你", "我").replace("吗", "");
        WebSocketUsers.sendMessageToUserByText(session, msg);
    }
}

2.4 WebSocketUsers tool class

Provide some simple tools


import java.io.IOException;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import javax.websocket.Session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * websocket 客户端用户集
 * 
 * @author ruoyi
 */
public class WebSocketUsers
{
    
    
    /**
     * WebSocketUsers 日志控制器
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketUsers.class);

    /**
     * 用户集
     */
    private static Map<String, Session> USERS = new ConcurrentHashMap<String, Session>();

    /**
     * 存储用户
     *
     * @param key 唯一键
     * @param session 用户信息
     */
    public static void put(String key, Session session)
    {
    
    
        USERS.put(key, session);
    }

    /**
     * 移除用户
     *
     * @param session 用户信息
     *
     * @return 移除结果
     */
    public static boolean remove(Session session)
    {
    
    
        String key = null;
        boolean flag = USERS.containsValue(session);
        if (flag)
        {
    
    
            Set<Map.Entry<String, Session>> entries = USERS.entrySet();
            for (Map.Entry<String, Session> entry : entries)
            {
    
    
                Session value = entry.getValue();
                if (value.equals(session))
                {
    
    
                    key = entry.getKey();
                    break;
                }
            }
        }
        else
        {
    
    
            return true;
        }
        return remove(key);
    }

    /**
     * 移出用户
     *
     * @param key 键
     */
    public static boolean remove(String key)
    {
    
    
        LOGGER.info("\n 正在移出用户 - {}", key);
        Session remove = USERS.remove(key);
        if (remove != null)
        {
    
    
            boolean containsValue = USERS.containsValue(remove);
            LOGGER.info("\n 移出结果 - {}", containsValue ? "失败" : "成功");
            return containsValue;
        }
        else
        {
    
    
            return true;
        }
    }

    /**
     * 获取在线用户列表
     *
     * @return 返回用户集合
     */
    public static Map<String, Session> getUsers()
    {
    
    
        return USERS;
    }

    /**
     * 群发消息文本消息
     *
     * @param message 消息内容
     */
    public static void sendMessageToUsersByText(String message)
    {
    
    
        Collection<Session> values = USERS.values();
        for (Session value : values)
        {
    
    
            sendMessageToUserByText(value, message);
        }
    }

    /**
     * 发送文本消息
     *
     * @param session 缓存
     * @param message 消息内容
     */
    public static void sendMessageToUserByText(Session session, String message)
    {
    
    
        if (session != null)
        {
    
    
            try
            {
    
    
                session.getBasicRemote().sendText(message);
            }
            catch (IOException e)
            {
    
    
                LOGGER.error("\n[发送消息异常]", e);
            }
        }
        else
        {
    
    
            LOGGER.info("\n[你已离线]");
        }
    }
}

2.5 html

<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>测试界面</title>
</head>

<body>

<div>
    <input type="text" style="width: 20%" value="ws://127.0.0.1/websocket/message" id="url">
	<button id="btn_join">连接</button>
	<button id="btn_exit">断开</button>
</div>
<br/>
<textarea id="message" cols="100" rows="9"></textarea> <button id="btn_send">发送消息</button>
<br/>
<br/>
<textarea id="text_content" readonly="readonly" cols="100" rows="9"></textarea>返回内容
<br/>
<br/>
<script th:src="@{/js/jquery.min.js}" ></script>
<script type="text/javascript">
    $(document).ready(function(){
      
      
        var ws = null;
        // 连接
        $('#btn_join').click(function() {
      
      
        	var url = $("#url").val();
            ws = new WebSocket(url);
            ws.onopen = function(event) {
      
      
                $('#text_content').append('已经打开连接!' + '\n');
            }
            ws.onmessage = function(event) {
      
      
                $('#text_content').append(event.data + '\n');
            }
            ws.onclose = function(event) {
      
      
                $('#text_content').append('已经关闭连接!' + '\n');
            }
        });
        // 发送消息
        $('#btn_send').click(function() {
      
      
            var message = $('#message').val();
            if (ws) {
      
      
                ws.send(message);
            } else {
      
      
                alert("未连接到服务器");
            }
        });
        //断开
        $('#btn_exit').click(function() {
      
      
            if (ws) {
      
      
                ws.close();
                ws = null;
            }
        });
    })
</script>
</body>
</html>

insert image description here

2.6 Vue version front-end code

url: "ws://localhost:8080/websocket
Our local configuration is: ws://127.0.0.1:18080/websocket/message
ws/wss cannot be modified, ip is the local connection address and ip websocket/messageis the default calling ruoyiinterface WebSocketServer;

You need to configure the address of the gateway if you use the gateway or the like; you can access all the addresses of the gateway in the previous section;

For the microservice project, remember to write the port. I must write it here, otherwise the access may not be possible because of the gateway;

//data中定义变量
lockReconnect: false,
      wsCfg: {
    
    
        // websocket地址(需要更改连接地址)
        url: "ws://localhost:8080/websocket
},
//method中定义方法

 createWebSocket() {
    
    
      console.log('createWebSocket')
            try {
    
    
                // 创建Web Socket 连接
                socket = new WebSocket(this.wsCfg.url);
                // 初始化事件
                this.initEventHandle(socket);
            } catch (e) {
    
    
                // 出错时重新连接
                console.log(e)
                this.reconnect(this.wsCfg.url);
            }
        },
    initEventHandle(socket) {
    
    
            // 连接关闭时触发
            socket.onclose = () => {
    
    
                console.log("连接关闭");
            };
            // 通信发生错误时触发
            socket.onerror = () => {
    
    
                // 重新创建长连接
                this.reconnect();
            };
            // 连接建立时触发
            socket.onopen = () => {
    
    
                console.log("连接成功");
            };
            // 客户端接收服务端数据时触发
            socket.onmessage = msg => {
    
    
                // 业务逻辑处理
              try{
    
    
                this.websocketSetFlag =	JSON.parse(msg.data).content;
                console.log(this.websocketSetFlag )
              }catch{
    
    

              }
						  
            };
        },
    reconnect() {
    
    
            //重连
            if (this.lockReconnect) {
    
    
                return;
            }
            this.lockReconnect = true;
            // 没连接上会一直重连,设置延迟避免请求过多
            setTimeout(() => {
    
    
                this.lockReconnect = false;
                this.createWebSocket(this.wsCfg.url);
            }, 2000);
        },
    sendWebsocket(text){
    
    
        //发送数据
        socket.send(
        JSON.stringify({
    
    
            type: "control",
            content: text,
        })
      ) ;
    },

2.7 controller

@Controller
@RequestMapping("/user")
public class HelloController {
    
    

    // websocket测试
    @GetMapping("/ws")
    public String ws()
    {
    
    
        return "websocket";
    }
}

insert image description here
For embedding the microservice project, I put it directly in the fixed module;

insert image description here
This article is only applicable to novices for the time being;

Extension: This article is also good for single multiple connections

Guess you like

Origin blog.csdn.net/qq_42055933/article/details/130985163