websocket存储session(适合聊天,转发,广播,服务器主动发送消息)

Websocket相关问题总结(Session共享,用户多端登录等) 我们在使用websocket的时候其实主要面对的问题就是session共享的问题: 不管是基于Spring实现的Websocket的WebsocketSession 还是基于JDK实现的Session 亦或者基于netty实现的ChannelHandlerContext 用图来描述下场景吧: OK,大家看到这个图了,差不多应该明白了Session共享应该怎么处理了。其实原理很简单: ... 阅读详情
AI摘要

本文介绍了一种使用Spring Boot封装的WebSocket框架来管理WebSocket中的Session信息的方法。通过将Session信息存储到Redis中,实现了用户间的互动与查找功能。文章还详细介绍了如何在连接建立、关闭及消息处理时进行Session的操作。

此篇文章主要分享的是websocket存储session信息,在网上经常看到有websocket的例子,此篇文章只是将websocket中的session提取并存储起来,便于用户之间的互动与查找,其实就是解耦合

此实现采用的是springboot的spring封装的websocket框架,原有的tomcat其实也可以完成

首先,spring框架封装的websocket有几种对应

onopen对应afterConnectionEstablished

onclose对应afterConnectionClosed

onMessage对应handleTextMessage

以下是处理消息类

@Component
public class DoubleTeachWSHandler extends TextWebSocketHandler {

    private static final Logger LOG = LoggerFactory
            .getLogger(DoubleTeachWSHandler.class);

    /**
     * 创建连接
     *
     * @param session 用户session
     * @throws Exception
     * @version v1.0
     * @autho 张瑞
     * @date 2017年12月18日 下午3:48:34
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) {
        System.out.println("afterConnectionEstablished");
        try {
            //获取用户信息
            SocketUser socketUser = DoubleTeachUserService
                    .getSocketUser(session);
            if (null == socketUser) {
                return;
            }
            //加入redis
            DoubleTeachUserService.addRedis(socketUser);
            //创建房间并加入session
            DoubleTeachSocketRoom.creatRoom(session, socketUser);
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        }
    }

    /**
     * 关闭链接
     *
     * @param session 用户session
     * @param status 状态
     * @throws Exception
     * @version v1.0
     * @autho 张瑞
     * @date 2017年12月18日 下午3:49:04
     */
    @Override
    public void afterConnectionClosed(WebSocketSession session,
            CloseStatus status) {
        System.out.println("afterConnectionClosed");
        try {
            //获取用户信息
            SocketUser socketUser = DoubleTeachUserService
                    .getSocketUser(session);
            if (null == socketUser) {
                return;
            }
            //删除缓存
            DoubleTeachUserService.deleteRedis(socketUser);
            //退出房间
            DoubleTeachSocketRoom.removeUserSession(socketUser);
            //是否删除房间
            DoubleTeachSocketRoom.deleteRoom(socketUser.getRoomID());
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        }
    }

    /**
     * 处理消息
     *
     * @param session 用户session
     * @param message message消息
     * @throws Exception
     * @version v1.0
     * @autho 张瑞
     * @date 2017年12月18日 下午3:55:18
     */
    @Override
    protected void handleTextMessage(WebSocketSession session,
            TextMessage message) {
        try {
            //获取用户信息
            SocketUser socketUser = DoubleTeachUserService
                    .getSocketUser(session);
            if (null == socketUser) {
                return;
            }
            String str = message.getPayload();
            if (StringUtils.isNull(str)) {
                //空消息不做操作
                return;
            }
            //进行解析
            SocketMessage socketMessage = JSONUtils.parse2Bean(str,
                    SocketMessage.class);
            DoubleTeachSendMsg.mainSendMessage(socketMessage,socketUser);
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        }
    }

}

此处是重点,将session统一存储到此类中,用map存储起来,并自定义key值,value即存储websocket使用的session

@Component
public class DoubleTeachSocketRoom {

    private static final Logger LOG = LoggerFactory
            .getLogger(DoubleTeachSocketRoom.class);

    //整体管理room的map
    private static ConcurrentHashMap<String, SocketRoom> roomMap = new ConcurrentHashMap<String, SocketRoom>(
            10000);

    /**
     * 获取用户session
     *
     * @param socketUser 用户实体
     * @return 用户session
     * @version v1.0
     * @author 张瑞
     * @date 2018年1月24日 上午10:51:11
     */
    public static WebSocketSession getUserSession(SocketUser socketUser) {
        try {
            if (null == socketUser) {
                return null;
            }
            SocketRoom socketRoom = roomMap.get(socketUser.getRoomID());
            if (null == socketRoom) {
                return null;
            }
            WebSocketSession session = socketRoom.getSessionMap()
                    .get(socketUser.getSessionID());
            return session;
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
            return null;
        }
    }

    /**
     * 移除用户session
     *
     * @param socketUser 用户实体
     * @version v1.0
     * @author 张瑞
     * @date 2018年1月24日 上午10:51:40
     */
    public static void removeUserSession(SocketUser socketUser) {
        try {
            if (null == socketUser) {
                return;
            }
            SocketRoom socketRoom = roomMap.get(socketUser.getRoomID());
            if (null == socketRoom) {
                //不需要做操作
                return;
            }
            socketRoom.getSessionMap().remove(socketUser.getSessionID());
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        }
    }

    /**
     * 获取当前房间用户总体
     *
     * @param roomID 房间ID
     * @return 房间实体
     * @version v1.0
     * @author 张瑞
     * @date 2018年1月24日 上午10:52:24
     */
    public static SocketRoom getSocketRoom(String roomID) {
        try {
            if (StringUtils.isNull(roomID)) {
                return null;
            }
            SocketRoom socketRoom = roomMap.get(roomID);
            return socketRoom;
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
            return null;
        }
    }

    /**
     * 添加用户进入房间并是否创建房间
     *
     * @param session 用户session
     * @param socketUser 用户
     * @version v1.0
     * @author 张瑞
     * @date 2017年12月19日 下午3:44:27
     */
    public static synchronized void creatRoom(WebSocketSession session,
            SocketUser socketUser) {
        SocketRoom socketRoom = roomMap.get(socketUser.getRoomID());
        if (null == socketRoom) {
            socketRoom = new SocketRoom();
            roomMap.put(socketUser.getRoomID(), socketRoom);
        }
        socketRoom.getSessionMap().put(session.getId(), session);
    }

    /**
     * 删除房间
     *
     * @param roomID
     * @version v1.0
     * @author 张瑞
     * @date 2017年12月18日 下午3:54:13
     */
    public static synchronized void deleteRoom(String roomID) {
        SocketRoom socketRoom = roomMap.get(roomID);
        if (null == socketRoom) {
            return;
        }
        if (socketRoom.getSessionMap().size() == 0) {
            roomMap.remove(roomID);
        }
    }

}

聊天室专用的存储表

public class SocketRoom implements Serializable {

    private ConcurrentHashMap<String, WebSocketSession> sessionMap;

    public SocketRoom() {
        sessionMap = new ConcurrentHashMap<String, WebSocketSession>(10000);
    }

    public SocketRoom(int size) {
        sessionMap = new ConcurrentHashMap<String, WebSocketSession>(size);
    }

    public ConcurrentHashMap<String, WebSocketSession> getSessionMap() {
        return sessionMap;
    }

    public void setSessionMap(
            ConcurrentHashMap<String, WebSocketSession> sessionMap) {
        this.sessionMap = sessionMap;
    }

}

用户存储表

public class SocketUser implements Serializable {

    private String token;//用户登录唯一验证
    private String sessionID;//websocketID
    private String roomID;//房间号
    private String liveid;//直播号

    public String getToken() {
        return token;
    }

    public void setToken(String token) {
        this.token = token;
    }

    public String getSessionID() {
        return sessionID;
    }

    public void setSessionID(String sessionID) {
        this.sessionID = sessionID;
    }

    public String getRoomID() {
        return roomID;
    }

    public void setRoomID(String roomID) {
        this.roomID = roomID;
    }

    public String getLiveid() {
        return liveid;
    }

    public void setLiveid(String liveid) {
        this.liveid = liveid;
    }

}

上述步骤主要将处理消息和session分离开,唯一要注意的是移动端的websocket在退出时服务端可能接收不到,一定要注意将session清空。否则session会越寸越大导致内存溢出

附加一个发送消息类

@Component
public class DoubleTeachSendMsg {
    private static final Logger LOG = LoggerFactory
            .getLogger(DoubleTeachSendMsg.class);

    /**
     * 发送消息给用户端的人
     *
     * @param socketMessage 消息
     * @param socketUser 用户
     * @throws IOException
     * @version v1.0
     * @author 张瑞
     * @date 2017年12月19日 下午5:15:56
     */
    private static void sendToClient(SocketMessage socketMessage,
            SocketUser socketUser)
        throws IOException {
        //获取当前端内用户
        List<String> list = Jedis.lrange(
                JedisKeys.userlist,
                socketUser.getLiveid() + ":" + socketUser.getCnum() + ":"
                        + socketMessage.getTo(),
                null, null);
        if (null == list || list.isEmpty()) {
            return;
        }
        //添加返回的json格式的字符串
        String jsonStr = JSONUtils.getJSON(socketMessage);
        //TODO
        LOG.error("to:socketMessage:" + jsonStr);
        for (String key : list) {
            //获取用户
            SocketUser socketUserThis = JSONUtils.parse2Bean(Jedis
                    .get(JedisKeys.USERINFO, key),
                    SocketUser.class);
            if (null == socketUserThis) {
                continue;
            }
            WebSocketSession session = DoubleTeachSocketRoom
                    .getUserSession(socketUserThis);
            if (null != session) {
                Jedis.lpush(
                        JedisKeys.SENDMESSAGE_QUEUE,
                        session.getId(), jsonStr);
                sendMessage(session);
            }
        }
    }

    /**
     * 发送消息(每个用户有自己的发送消息锁)
     *
     * @param session
     * @version v1.0
     * @author 张瑞
     * @date 2018年1月6日 下午1:55:40
     */
    public static void sendMessage(WebSocketSession session) {
        String msg = "";
        try {
            Long lock = Jedis.setnx(
                    JedisKeys.SENDMESSAGE_LOCK,
                    session.getId(), "1");
            if (lock == 1) {
                //循环发送消息
                while (true) {
                    msg = Jedis.rpop(
                            JedisKeysEnum.SENDMESSAGE_QUEUE,
                            session.getId());
                    if (StringUtils.isNull(msg)) {
                        //删除锁
                        Jedis.delete(
                                JedisKeysEnum.SENDMESSAGE_LOCK,
                                session.getId());
                        return;
                    }
                    //判断链接是否存在
                    TextMessage textMessage = new TextMessage(msg);
                    session.sendMessage(textMessage);
    //服务器可能判断isopen不准,简化步骤,不采用,遇到问题直接结束websocket链接
                    /*if (session.isOpen()) {
                        synchronized (session) {
                            session.sendMessage(new TextMessage(msg));
                        }
                    } else {
                        Jedis.delete(
                                JedisKeys.SENDMESSAGE_LOCK,
                                session.getId());
                        Jedis.delete(
                                JedisKeysEnum.SENDMESSAGE_QUEUE,
                                session.getId());
                        //调用结束逻辑
                        session.close();
                    }*/
                }
            }
        } catch (Exception e) {
            //由于客户端断开服务器监听不到,在客户端简历重连机制,发送失败即断开
            //删除锁
            Jedis.delete(JedisKeys.SENDMESSAGE_LOCK,
                    session.getId());
            Jedis.delete(
                    JedisKeys.SENDMESSAGE_QUEUE,
                    session.getId());
            LOG.error("error_msg_is:" + msg);
            LOG.error(e.getMessage(), e);
            try {
                session.close();
            } catch (Exception ex) {
                LOG.error(ex.getMessage(), ex);
            }
        }
    }

}

此为分离websocket的session的实现,有些代码涉及到了业务,进行了删除,理解了整体思路即可。这样,独立出了session信息,在服务器层遇到接口只要能获取存储session的key值,便可发送消息,实现主动发送消息给客户端
spring mvc 实现websocket(服务器主动消息推送) 看了好多网上能搜到的spring mvc实现websocket的教程,总算淌出了坑,搞出来一个比较纯粹的实现websocket的Demo。   项目运行后,可以启动一个页面进行登录,然后在另一个页面中向服务器发送请求,服务器再向登录的页面推送消息。 启动页面登录:http://127.0.0.1:8080/jyl/login/jyl login后是用户名,登录后页面效果如图   再... 阅读详情

相关推荐

vue使用websocket

websocket其他特点如下: (1)建立在 TCP 协议之上,服务器端的实现比较容易。 (2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。 (3)数据格式比较轻量,性能开销小,通信高效。 (4)可以发送文本,也可以发送二进制数据。 (5)没有同源限制,客户端可以与任意服务器通信...

高山景行,一尘不染 1134

Spring Boot 中的 WebSocketSession 是什么,原理,如何使用

WebSocketSession 是 Spring Boot 中用于与客户端进行 WebSocket 通信的核心概念。在使用 WebSocketSession 时,我们需要先添加 Spring Boot 的 WebSocket 依赖,然后配置 WebSocket,并实现一个 WebSocket 处理器,最后在处理器中使用 WebSocketSession 进行通信。客户端也可以使用 WebSocketSession 进行通信,非常简单。

程序猿徐师兄的博客 9075

JavaWeb一对一网页聊天系统实战:WebSocket会话绑定与消息可靠投递

WebSocket 是一种在 TCP 之上通过 HTTP 握手升级的全双工通信协议,解决了 HTTP 无状态下服务器无法主动推送的难题。其核心原理在于握手阶段复用 Cookie/Session 完成身份绑定,升级后建立持久连接,实现毫秒级消息推送。在 JavaWeb 项目中,WebSocket 的技术价值尤为突出,尤其是一对一聊天场景,必须精确处理会话绑定、在线状态同步与离线消息存储,远比群聊复杂。典型应用包括即时通讯、在线客服与协同编辑。本文围绕一个可运行的 JavaWeb 项目完整案例,深入讲解 Web

weixin_34021089的博客 406

Websocket Session 共享解决

Websocket Session 共享解决(方案一) 既然Websocket Session不能直接序列化然后存储,而且如果用户请求的时候,开始与集群中的A节点创建链接,就算把这个Session 拿到B节点去再给用户Push消息,应该也是不通(没测试)的。 所以我的解决方案采用订阅式队列去触发Websocket 反推的方式来实现看上去是Session共享 的,思路如下图: Websocket Session共享 思路图解释: 模块一:这里没有什么好说明的,因为每一...

qq_27276663的博客 6534

九天江湖聊天室源码解析:WebSocket实时通信与Spring Boot部署实践

在Web应用开发中,实时通信是关键需求,而WebSocket协议为浏览器与服务端提供了全双工通信能力,是实现聊天室、在线协作等场景的基础。Spring Boot作为主流Java框架,结合Vue构建前后端分离的轻量级架构,既能保证工程规范性,又降低了维护成本。本文从聊天室系统的核心需求出发,剖析一套武侠主题的开源工程,涵盖用户体系、房间管理、消息路由等模块设计,重点讲解WebSocket连接建立、心跳保活、断线重连及私聊广播机制。同时,针对部署上线环节,详细介绍了Nginx反代配置、MySQL时区问题、Red

weixin_33971977的博客 531

从零搭建JavaWeb一对一聊天系统:WebSocket实战与避坑指南

WebSocket是一种基于TCP的全双工通信协议,通过一次HTTP握手建立持久连接,实现服务端与客户端的实时双向消息推送。其核心原理在于握手升级与帧协议,相比轮询方案能显著降低延迟与服务器压力。在JavaWeb开发中,WebSocket常用于在线聊天、实时通知等场景。一对一聊天系统需要处理用户身份绑定、会话管理、消息路由与离线存储等关键问题。本文基于SpringBoot与WebSocket,详细讲解一对一网页聊天系统的完整实现,包括数据库设计、Session管理、消息协议与常见部署避坑,帮助开发者掌握实时

weixin_34342992的博客 227

WebSocket 集群 session 共享方案

集群式聊天室session及群发消息初步方案问题引入思路分析代码实现 !! 本文聊天室基于 websocket 进行实现,同时也为解决websocket session在集群部署服务时的无法共享导致的收发消息问题。 问题引入     当我们使用 websocket 实现聊天时,后端服务会将所有的 websocket session缓存起来,之后根据收到的消息,遍历或者找到某个session进行消息的发送。 但是在我们进行后端集群部署时,这里假设存在2台后端服务器A

qq_38245668的博客 3157

Spring Boot+WebSocket实战:构建可靠的多人聊天系统

实时通信是互联网应用的重要基础设施,不同业务场景对实时性和双向交互的要求差异很大。WebSocket凭借全双工、低延迟的特性,成为多人聊天、协同编辑等场景最常用的技术方案。但要实现一个可靠的生产级聊天系统,仅有连接还不够,还需要解决消息不丢、不乱、不重以及离线补发等核心问题。Spring Boot作为后端开发的主流框架,结合Redis Pub/Sub与持久化存储,为连接管理、消息广播和集群扩展提供了成熟路径。本文从实时通信选型切入,系统拆解多人聊天系统的消息协议、心跳机制、序号分配及避坑要点,完整呈现从单机

weixin_30074763的博客 305

在线聊天室(Vue+Springboot+WebSocket)

实现了一个简单的在线聊天室的前后端。前端用Vue实现,后端用Springboot实现,并且用到了WebSocket。

qq_41194924的博客 4333

代码审计工具学习之Seay(安装以及初步认识)

目录 1.1名词解释 1.2代码审计的重要性 1.3代码审计的步骤 1.4代码审计的内容 1.5常见的代码审计工具 2 Seay 2.1什么是Seay 2.2Seay的安装过程 3典型漏洞分析 3.1什么是命令注入漏洞 3.2 如何理解命令注入漏洞 3.3 如何防御命令注入漏洞 4 利用工具对命令注入漏洞进行分析 4.1DVWA靶场命令注入漏洞分析 4.2 Pikachu靶场命令注入漏洞分析 1.1名词解释 软件代码审计是对编程项目中源代码的全面...

weixin_52515588的博客 2万+

基于Python API的ECMWF大气数据批量下载与自动化处理实践

本文详细介绍了如何利用Python API实现ECMWF大气数据的自动化批量下载与处理。针对遥感分析中手动下载ERA5等数据的痛点,文章提供了从API密钥配置、核心脚本编写到元数据解析、任务队列调度及数据质量检查的全流程实践指南,旨在帮助科研与工程人员高效获取并处理大规模气象数据。

u8v9w0x1y的博客 479

SpringBoot-WebSocket广播消息+单点消息(指定用户发送消息)

前言 关于Springboot中WebSocket的介绍这里就不赘述了。网上还是有比较多的介绍。本文主要解决的问题是广播消息(某个主题的),特定用户的消息发送。废话不多说,开始我们的实现。 环境 这里只罗列核心的技术点。 jdk :1.8 maven :3.X Spring boot:1.5.2.RELEASE WebSocket:spring-boot-starter-websock...

weixin_30883271的博客 250

为什么需要知识图谱?什么是知识图谱?——KG的前世今生

我的知乎专栏,对知识图谱感兴趣的朋友可以关注。标题的命名顺序可能让有的读者不太习惯。通常在介绍一个陌生事物的应用前,我们先给出其定义。之所以换个顺序,是为了不让读者一开始就接触比较冰冷生硬的概念刻板描述(后面我尽量用更具体、准确的例子来表达),另一方面也是为了通过现实生活中的例子自然的引入知识图谱的概念。希望通过这种方式加深读者的印象和理解。为了减轻读者理解的负担,我尽可能地避免引入过多的概念和技术

SimmerChan的专栏 8万+

炫技亮点 Websocket集群解决方案汇总

WebSocket的连接在不同的服务器之间是无法共享的,因此无法将WebSocket的Session存储在共享的存储中。通过将消息发送到中间件,然后再将消息广播给订阅的服务器,类似于广播的方式,只要订阅了消息,就能接收到消息的通知。例如,在一个WebSocket集群中,当一个客户端连接到节点A时,而其他节点(B、C)不知道这个连接存在,当节点B收到一条消息并尝试将其推送给该客户端时,它会失败,因为它不知道该客户端连接到了节点A。在集群中的每个服务节点上发送群发消息,以确保消息能够覆盖到所有连接的客户端。

laker的博客 5974

【数据集6】全球人工不透水面积GAIA(清华数据)

全球人工不透水面GAIA简介及其应用

WW、forever的博客 3803

【异常】安装OpenClaw遇到 Windows环境pnpm安装node-llama-cpp postinstall生命周期脚本执行失败完整解决方案

摘要: Windows环境下使用pnpm安装node-llama-cpp时,postinstall脚本执行失败(错误码3221225477),主要因缺少C++编译工具链或预构建二进制下载失败。解决方案: 基础排查:确保管理员权限、Node.js ≥18、路径无中文; 安装编译工具:推荐Visual Studio的“C++桌面开发”组件或windows-build-tools; 环境变量配置:强制CPU模式+国内镜像(NODE_LLAMA_CPP_FORCE_CPU=true,镜像地址替换为国内源); pnp

奶爸的编程之路,也就一周冷个三天 619

网络编程 websocket聊天室demo

        通过websocket实现聊天室主要是将消息进行广播,把消息发送给其他的用户会话中去,通过消息推送,推送到每一个session里面去。         通过一个静态共享变量如List获取所有的session通道,然后遍历这个List,把消息广播出去。 public void broadcast(List&lt;Session&gt; sessions,String msg) ...

闲_风的博客 574

知识图谱构建中的数据清洗:AI原生应用高质量图谱的前提

数据清洗是知识图谱构建的核心前置环节,直接决定AI原生应用(如智能问答、个性化推荐、决策支持系统)的图谱质量与应用效能。本文以第一性原理为起点,系统解析数据清洗的理论框架、技术架构与工程实践,覆盖从多源异构数据的噪声检测到高质量知识三元组(实体-关系-实体)生成的全流程。通过层次化概念映射(专家→中级→入门)、数学形式化建模及工业级案例分析,揭示数据清洗在解决实体歧义、关系冲突、结构不一致等关键问题中的技术逻辑,并展望基于大模型的智能清洗范式演化。Completeness(完整性)

移动开发前沿的博客 404

springboot+websocket 上手学习项目 简易聊天室

ServerSocket 适用于传统的TCP网络编程,基于TCP协议,采用请求 - 响应模式,适合对数据可靠性要求较高的场景。- WebSocket 适用于实时通信场景,基于WebSocket协议,支持全双工通信,能够实现浏览器和服务器之间的实时数据交互。

zhengzhiyu__的博客 858

【安全机制分析案例】从PDF预览页面提取原始PDF文件的方法原理与实践 篇二:基于致远互联(Seeyon)封装的SuwellLightRead技术

该HTML代码片段展示了致远OA协同办公系统的前端页面结构,包含多个CSS样式表和JavaScript文件的引用。页面设置了IE兼容模式,加载了全局环境变量、UI组件库和公告模块相关资源。代码中定义了系统路径、静态资源后缀等全局变量,并包含公告查看功能的相关配置参数。页面还引用了水印、文档处理、即时通讯等功能的JS文件,并设置了文件下载框架。整体呈现了一个典型的OA系统前端实现方案,包含浏览器兼容处理、资源加载和功能模块初始化等逻辑。

生活在别处 2074

OCR文字识别领域经典论文总结

本篇将介绍文字识别经典论文,内容包括文字检测、文字识别、端到端识别等方法,具体将分别对论文算法简介、思路、代码等几个部分展开介绍。目前各部分只举例经典论文,算法没有全部写完,后续会将在此部分基础上更新更多论文综述及代码实战部分。.................................

专注大数据与人工智能技术分享,欢迎私信加群互相学习! 1万+

WebSocket-练习1

基于Websocket实现的聊天室项目,后端代码如下,讲解了Websocket的基本使用,以及解释使用Websocket时如何拿到握手阶段的httpsession并存入websocket配置类对象中

weixin_74399364的博客 1232

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

Crow WebSocket实时通信:构建高性能聊天应用的10个实用技巧

Crow是一款快速且易于使用的Web微框架,它为开发者提供了构建实时Web应用的强大能力。本文将分享10个实用技巧,帮助你利用Crow的WebSocket功能构建高性能的聊天应用。 [![Crow微框架Logo](https://raw.gitcode.com/gh_mirrors/cro/Crow/raw/c61a26ee2e5f63735e6c9815d8f7e8c611a3f336/doc

gitblog_01092的博客 884

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

WebSocket+SpringBoot聊天室(二)

目录 1.用户离开聊天室 2.单聊和多聊 3.项目源码 1.用户离开聊天室 跟用户进入聊天室一样,用户离开聊天室会触发OnClose,在OnClose广播我将要离开就可以了,思路跟进入聊天室是一样的,只是广播的内容变了。 @OnClose public void onClose(){ webSockets.remove(this); map...

啊林博客 1685

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

如何用Chalice写WebSocket应用:从Echo回显到实时聊天全攻略

Chalice 是 AWS 官方出品的 Python Serverless 微框架(Python Serverless Microframework for AWS),用它开发 WebSocket 应用只需要三行装饰器。本文带你从最简单的 Echo 回显服务器写起,再进阶到带房间、昵称管理的多用户实时聊天室,全程基于 AWS API Gateway WebSocket API + Lambda,无

gitblog_00140的博客 201

如何用galaxy-downloader保存小红书与Instagram图文笔记:图文下载与打包导出教程

galaxy-downloader 是一款免费通用的媒体下载工具,除了 Bilibili、抖音、TikTok 等平台的视频下载外,还支持小红书图文笔记、Instagram 帖子与 Reels 图文的完整保存。粘贴笔记或帖子链接后,所有图片会以网格形式预览,可以逐张下载,也可以一键打包导出为 ZIP 压缩包,非常适合想批量保存灵感、素材或回忆的朋友。 [![galaxy-downloader 通用

gitblog_00189的博客 1034
上一篇: JAVA程序员学python-3.函数
下一篇: websocket影响内存泄漏的两个点
ZERO_No1
博客等级 码龄12年 14粉丝 14原创
评论 16
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值