集成websocket实现实时通信(ruoyi 使用笔记)

简介: 集成websocket实现实时通信(ruoyi 使用笔记)

1.简单介绍WebSocket

Websocket 是一种基于 TCP 协议的全双工通信协议,它使得客户端和服务器之间可以进行实时的双向通信。相对于传统的 HTTP 协议只能通过客户端发送请求,然后等待服务端的响应,WebSocket 可以让客户端和服务器在任何时候都可以相互发送消息,这种实时通信的方式非常适合需要实时更新数据的应用场景,比如聊天室、在线游戏、股票行情等。

WebSocket 的运作流程如下:

客户端向服务器发起 WebSocket 握手请求;

服务器返回确认信息给客户端,完成握手;

握手成功后,客户端和服务器就可以通信了;

双方可以随时发送消息到对方,也可以关闭连接。

WebSocket 的好处包括:

实时性:WebSocket 提供了双向通信能力,可以实现实时更新数据的功能;

可靠性:WebSocket 基于 TCP 协议,可以保证消息传输的可靠性;

性能高:WebSocket 的开销小,通信效率高,不会频繁地进行连接、断开等操作,降低网络延迟;

跨域支持:WebSocket 支持跨域通信,可以在不同的域之间建立连接。

在前端开发中,使用 WebSocket 可以使用 JavaScript WebSocket API 来进行操作,常见的库包括 Socket.io 和 WebSocket-Node 等。在后端开发中,WebSocket 的实现可以使用 Node.js、Java、Python 等多种语言和框架。

当然,在使用 WebSocket 时也需要考虑一些安全问题,比如避免跨站脚本攻击(XSS)、防范恶意请求等。

若依网址点击即可;

链接: https://pan.baidu.com/s/13JVC9jm-Dp9PfHdDDylLCQ 提取码: y9jt

2.详细代码

(大家可以去若依下载我自己做下记录而已)

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工具类

提供一些简单的工具

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>

2.6 vue版本前端代码

url: "ws://localhost:8080/websocket

我们本地配置为:ws://127.0.0.1:18080/websocket/message

ws/wss 不可修改,ip为本地需要连接地址ip websocket/message 为默认调用ruoyiWebSocketServer 接口;

大家有使用网关之类的需要配置网关的地址;前段去访问网关的全部的地址即可;

微服务项目记得写端口我这边必须写不然访问不到 可能因为么走网关的原因;

//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";
    }
}

嵌入微服务项目我是直接把他直接放在固定模块中的;

此文章暂时只适用于新手;

扩展:单个多个连接这个文章也不错


相关文章
|
4月前
|
前端开发 JavaScript 网络协议
深入理解Python Web开发中的前后端分离与WebSocket实时通信技术
【7月更文挑战第18天】前后端分离采用Flask/Django框架,前端JavaScript框架如Vue.js与后端通过AJAX/Fetch通信。WebSocket提供实时双向通信,Python可借助websockets库或Flask-SocketIO实现。最佳实践包括定义清晰的接口规范,确保安全性(HTTPS,认证授权),优化性能,和健壮的错误处理。结合两者,打造高效实时应用。
95 1
|
1月前
|
前端开发 JavaScript UED
探索Python Django中的WebSocket集成:为前后端分离应用添加实时通信功能
通过在Django项目中集成Channels和WebSocket,我们能够为前后端分离的应用添加实时通信功能,实现诸如在线聊天、实时数据更新等交互式场景。这不仅增强了应用的功能性,也提升了用户体验。随着实时Web应用的日益普及,掌握Django Channels和WebSocket的集成将为开发者开启新的可能性,推动Web应用的发展迈向更高层次的实时性和交互性。
76 1
|
22天前
|
Kubernetes Cloud Native JavaScript
为使用WebSocket构建的双向通信应用带来基于服务网格的全链路灰度
介绍如何使用为基于WebSocket的云原生应用构建全链路灰度方案。
|
1月前
|
前端开发 Java C++
RSocket vs WebSocket:Spring Boot 3.3 中的两大实时通信利器
本文介绍了在 Spring Boot 3.3 中使用 RSocket 和 WebSocket 实现实时通信的方法。RSocket 是一种高效的网络通信协议,支持多种通信模式,适用于微服务和流式数据传输。WebSocket 则是一种标准协议,支持全双工通信,适合实时数据更新场景。文章通过一个完整的示例,展示了如何配置项目、实现前后端交互和消息传递,并提供了详细的代码示例。通过这些技术,可以大幅提升系统的响应速度和处理效率。
|
1月前
|
监控 小程序 前端开发
小程序全栈开发中的WebSocket实时通信实践
【10月更文挑战第3天】随着移动互联网的发展,小程序因便捷的用户体验和社交传播能力,成为企业拓展业务的新渠道。本文探讨了小程序全栈开发中的WebSocket实时通信实践,包括其实时通信、长连接及双向通信的特点,并通过实时聊天、推送、游戏和监控等功能的实现,展示了WebSocket在小程序中的应用。开发者需注意安全性、性能及兼容性等问题,以保障小程序的稳定运行和用户体验。
45 7
|
1月前
|
安全 数据安全/隐私保护 UED
优化用户体验:前后端分离架构下Python WebSocket实时通信的性能考量
在当今互联网技术的迅猛发展中,前后端分离架构已然成为主流趋势,它不仅提升了开发效率,也优化了用户体验。然而,在这种架构模式下,如何实现高效的实时通信,特别是利用WebSocket协议,成为了提升用户体验的关键。本文将探讨在前后端分离架构中,使用Python进行WebSocket实时通信时的性能考量,以及与传统轮询方式的比较。
65 2
|
2月前
|
前端开发 JavaScript 安全
深入理解Python Web开发中的前后端分离与WebSocket实时通信技术
在现代Web开发中,前后端分离已成为主流架构,通过解耦前端(用户界面)与后端(服务逻辑),提升了开发效率和团队协作。前端使用Vue.js、React等框架与后端通过HTTP/HTTPS通信,而WebSocket则实现了低延迟的全双工实时通信。本文结合Python框架如Flask和Django,探讨了前后端分离与WebSocket的最佳实践,包括明确接口规范、安全性考虑、性能优化及错误处理等方面,助力构建高效、实时且安全的Web应用。
56 2
|
2月前
|
前端开发 Python
前后端分离的进化:Python Web项目中的WebSocket实时通信解决方案
在现代Web开发领域,前后端分离已成为一种主流架构模式,它促进了开发效率、提升了应用的可维护性和可扩展性。随着实时数据交互需求的日益增长,WebSocket作为一种在单个长连接上进行全双工通讯的协议,成为了实现前后端实时通信的理想选择。在Python Web项目中,结合Flask框架与Flask-SocketIO库,我们可以轻松实现WebSocket的实时通信功能。
57 2
|
2月前
|
JavaScript 前端开发 UED
WebSocket在Python Web开发中的革新应用:解锁实时通信的新可能
在快速发展的Web应用领域中,实时通信已成为许多现代应用不可或缺的功能。传统的HTTP请求/响应模式在处理实时数据时显得力不从心,而WebSocket技术的出现,为Python Web开发带来了革命性的变化,它允许服务器与客户端之间建立持久的连接,从而实现了数据的即时传输与交换。本文将通过问题解答的形式,深入探讨WebSocket在Python Web开发中的革新应用及其实现方法。
44 3
|
2月前
|
前端开发 API Python
WebSocket技术详解:如何在Python Web应用中实现无缝实时通信
在Web开发的广阔领域中,实时通信已成为许多应用的核心需求。传统的HTTP请求-响应模型在实时性方面存在明显不足,而WebSocket作为一种在单个长连接上进行全双工通信的协议,为Web应用的实时通信提供了强有力的支持。本文将深入探讨WebSocket技术,并通过一个Python Web应用的案例分析,展示如何在Python中利用WebSocket实现无缝实时通信。
64 2