websoket 学习笔记

news2025/4/16 1:32:36

目录

基本概念

工作原理

优势

应用场景

HTTP协议与 webSoket协议之间的对比

消息推送场景

1. 轮询(Polling)

2. 长轮询(Long Polling)

3. 服务器发送事件(Server-Sent Events, SSE)

4. WebSocket

服务端WebSoket

基本介绍

引入依赖

核心API示例

聊天消息demo流程分析

配置类

webSocketConfig(注入ServerEndpoint注解)

GetHttpSessionConfig(存储Session会话对象)

服务类

Server端Socket

客户端


WebSocket 是一种基于 TCP 的网络通信协议,允许在客户端和服务器之间建立持久的双向通信连接。

基本概念

  • 全双工通信(Full Duplex):WebSocket 支持客户端和服务器在同一连接上同时发送和接收数据,允许数据在两个方向上同时传输。

  • 半双工(Half Duplex):允许数据在两个方向上传输,但是同一个时间段内只允许一个方向上传输。

  • 持久连接:通过一次握手建立连接后,连接会一直保持,无需每次通信都重新建立

  • 低延迟与高效性:减少了传统 HTTP 请求-响应模式中的频繁连接开销,数据传输更高效

工作原理

  1. 握手阶段:客户端通过 HTTP 请求向服务器发起 WebSocket 升级请求,服务器响应后将连接升级为 WebSocket

  • 数据传输:连接建立后,数据以帧的形式在客户端和服务器之间传输

  • 关闭连接:当一方关闭连接时,WebSocket 会发送关闭帧,通知对方关闭连接

优势

  • 实时性:适合需要快速响应和更新的场景

  • 节省资源:减少了不必要的网络请求和响应,降低了带宽和服务器资源消耗

  • 跨平台支持:主流浏览器和服务器端语言均支持 WebSocket

应用场景

  • 实时通信:如在线聊天、实时游戏

  • 数据推送:服务器主动向客户端推送数据,如股票行情、天气预报

  • 实时监控:如视频监控、设备状态监控

        WebSocket 是现代 Web 开发中实现高效实时通信的重要技术,广泛应用于各种需要快速数据交互的场景。

HTTP协议与 webSoket协议之间的对比

        用到最多的其中一个场景就是 消息推送。

消息推送场景

消息推送是指服务器主动向客户端发送信息的技术。以下是几种常见的消息推送方式:

1. 轮询(Polling)

  • 原理:客户端每隔固定时间向服务器发送请求,查询是否有新消息。

  • 优点

    • 实现简单,兼容性好,不需要额外的服务器支持。

  • 缺点

    • 频繁请求会增加服务器负担,浪费带宽。

    • 实时性较差,消息延迟取决于轮询间隔。

  • 适用场景:对实时性要求不高的场景,如邮件通知。

2. 长轮询(Long Polling)

  • 原理:客户端向服务器发送请求,服务器保持连接打开,直到有新消息才返回响应。

  • 优点

    • 减少了无效请求,相比普通轮询更节省资源。

    • 实时性更好。

  • 缺点

    • 服务器需要保持大量连接,对服务器资源要求较高。

    • 如果消息间隔过长,可能导致连接超时。

  • 适用场景:对实时性有一定要求但不希望使用复杂技术的场景。

3. 服务器发送事件(Server-Sent Events, SSE)

原理

  • 服务器通过 HTTP 连接向客户端推送数据,客户端通过 EventSource 接收消息。

  • SSE在服务器和客户端之间打开一个单向通道

  • 服务端响应的不再是一次性的数据包,而是text/event-stream类型的数据流信息

  • 服务器有数据变更时将数据流式传输到客户端

优点

  • 实现简单,基于 HTTP,兼容性较好。

  • 只支持单向通信(服务器到客户端),适合通知类应用。

缺点

  • 不支持双向通信。

  • 不支持跨域,需要服务器和客户端在同一个域下。

适用场景:股票行情、新闻推送等单向通知场景。

4. WebSocket

  • 原理:通过一次握手建立持久的双向通信连接,客户端和服务器可以随时发送和接收消息。

  • 优点

    • 全双工通信,实时性高。

    • 数据传输效率高,减少了 HTTP 请求的开销。

  • 缺点

    • 实现复杂,需要服务器和客户端都支持 WebSocket 协议。

    • 需要额外处理连接的建立和关闭。

  • 适用场景:在线聊天、实时游戏、实时协作工具等。

不同消息推送方式各有优缺点,选择时需要根据具体需求和场景进行权衡。例如:

  • 如果对实时性要求不高,可以选择轮询或长轮询。

  • 如果需要高效实时的双向通信,WebSocket 是更好的选择。

服务端WebSoket

基本介绍

引入依赖

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

核心API示例

聊天消息demo流程分析

配置类

webSocketConfig(注入ServerEndpoint注解)

package com.angindem.server.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 {
    @Bean
//    注入ServerEndpointExporter,自动注册使用@ServerEndpoint注解的
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

GetHttpSessionConfig(存储Session会话对象)

package com.angindem.server.config;

import com.angindem.common.utils.CommonUtils;
import com.angindem.common.utils.IpUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import jakarta.websocket.HandshakeResponse;
import jakarta.websocket.server.HandshakeRequest;
import jakarta.websocket.server.ServerEndpointConfig;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

public class GetHttpSessionConfig extends ServerEndpointConfig.Configurator {
    @Override
    public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
//         获取 HttpSession 对象
        HttpSession httpSession = (HttpSession) request.getHttpSession();

        // 获取当前请求的HttpServletRequest
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest req = attributes.getRequest();
        
        
        if (httpSession == null) httpSession = req.getSession();
        String ip = IpUtils.getIpAddress(req);
        httpSession.setAttribute("user", CommonUtils.getRandomString(8));
        
//        将 HttpSession 对象存储到 ServerEndpointConfig 中
        sec.getUserProperties().put(HttpSession.class.getName(), httpSession);// 将HttpServletRequest存储到EndpointConfig的用户属性中

        sec.getUserProperties().put(String.class.getName(), ip);
    }
}

服务类

Server端Socket

package com.angindem.server.wx;
import com.alibaba.fastjson2.JSON;
import com.angindem.common.utils.MessageUtils;
import com.angindem.server.config.GetHttpSessionConfig;
import com.angindem.server.wx.pojo.Message;
import jakarta.servlet.http.HttpSession;
import jakarta.websocket.*;
import jakarta.websocket.server.ServerEndpoint;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
@Component
@ServerEndpoint(value = "/chat", configurator = GetHttpSessionConfig.class)
public class ChatEndPoint {

    //    ConcurrentHashMap 高并发的多线程访问的哈希Map
    public static final Map<String, Session> onlineUsers = new ConcurrentHashMap<>();
    private HttpSession httpSession;

    @OnOpen
    public void onOpen(Session session, EndpointConfig config) {
        log.info("连接建立成功");
//        1、将 Session 放入到集合中,方便后续使用
        this.httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
        String  reqIp = (String) config.getUserProperties().get(String.class.getName());

        String user =  MessageUtils.getNowSessionUserName(this.httpSession);
        onlineUsers.put(user, session);
//        2、广播消息。需要将登录的所有用户推送给所有用户
        log.info("当前在线人数:{}", onlineUsers.size());
        log.info("当前用户:{},来自:{}", user, reqIp);
        String message = MessageUtils.getDataMessage("system", user, MessageUtils.getFrinds());
        MessageUtils.broadcastAllUsers(message);
    }

    @OnMessage
    public void onMessage(String message) {
        log.info("收到消息:{}", message);
        Message msg = JSON.parseObject(message, Message.class);
        // 获取 消息接收方的用户名
        String toName = msg.getToName();
        String mess = msg.getMessage();
        String user =  MessageUtils.getNowSessionUserName(this.httpSession);
        String sendMessage = MessageUtils.getMessage("user", user, mess);
        // 获取消息接收方用户对象的 session 对象
//            Session session = onlineUsers.get(toName);
//            session.getBasicRemote().sendText(sendMessage);
        MessageUtils.broadcastAllUsers(sendMessage);
    }
    @OnClose
    public void onClose(Session session) {
        log.info("连接关闭");
//        1、从 onlineUsers 中剔除当前的 session 对象
        String user =  MessageUtils.getNowSessionUserName(this.httpSession);
        onlineUsers.remove(user);
//        2、通知其它所有的用户,当前用户已经下线
        log.info("当前在线人数:{}", onlineUsers.size());
        String message = MessageUtils.getDataMessage("system", user, MessageUtils.getFrinds());
        MessageUtils.broadcastAllUsers(message);
    }
}

客户端

(VueUse的  webSocket 客户端)

/**
 * status 是连接的状态值
 * data 是 socket 推送过来的消息
 * send 是一个方法, 用来发送消息给后台
 * close 是一个方法,关闭 socket 连接
 * open 如果当前的websocket是活跃的,将会关闭它重新打开一个新的
 */
const { status, data, close, open, send } = useWebSocket(`ws://localhost:9898/chat`, {
        onConnected: function (ws) {
            console.log('websocket 连接成功!', ws)
        },
        onDisconnected: function (ws, event) {
            console.log('onDisconnected')
        },
        onError: function (ws, event) {
            console.log('onError', event)
        },

        onMessage: function (ws, event) {
            console.log('event.data', event.data)
            if (event.data) {
                // messageInfo.value = []
                const info = JSON.parse(event.data)
                console.log(info) // 这里就是websocket每次发送的消息

                if (info.data) updateUsers(info.data);
                if (info.message) messageInfo.value.push(info)
            }
        },
        heartbeat: false,
        autoClose: false, // 自动关闭连接
    });

扩展:

同时可以通过 vue 中的 watch 监听发送过来的数据


import {  watch } from 'vue';
//获取数据时必须要监视,此处的data就是上面结构出的data
watch(data, () => {
  //获取到的数据为data.value
    console.log(data.value)
})

发送消息到服务端

//需要发送给服务器的数据
const info = "你好吗?"
 
//send方法,此处的send为上面解构出的send
send(info)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2335626.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

博途 TIA Portal之1200做从站与汇川EASY的TCP通讯

上篇我们写到了博途做主站与汇川EASY的通讯。通讯操作起来很简单,当然所谓的简单,也是相对的,如果操作成功一次,那么后面就很容易了, 如果操作不成功,就会很遭心。本篇我们将1200做从站,与汇川EASY做主站进行TCP的通讯。 1、硬件准备 1200PLC一台,带调试助手的PC机一…

【数据结构_6下篇】有关链表的oj题

思路&#xff1a; 1.分别求出这两个链表的长度 2.创建两个引用&#xff0c;指向两个链表的头节点&#xff1b;找到长度长的链表&#xff0c;让她的引用先走差值步数 3.让这两个引用&#xff0c;同时往后走&#xff0c;每个循环各自走一步 然后再判定两个引用是否指向同一个…

vscode+wsl 运行编译 c++

linux 的 windows 子系统&#xff08;wsl&#xff09;是 windows 的一项功能&#xff0c;可以安装 Linux 的发行版&#xff0c;例如&#xff08;Ubuntu&#xff0c;Kali&#xff0c;Arch Linux&#xff09;等&#xff0c;从而可以直接在 windows 下使用 Linux 应用程序&#xf…

关于 Spring Boot 微服务解决方案的对比,并以 Spring Cloud Alibaba 为例,详细说明其核心组件的使用方式、配置及代码示例

以下是关于 Spring Boot 微服务解决方案的对比&#xff0c;并以 Spring Cloud Alibaba 为例&#xff0c;详细说明其核心组件的使用方式、配置及代码示例&#xff1a; 关于 Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案! https://sca.aliyun.com/?spm7145af80…

VS 基于git工程编译版本自动添加版本号

目录 概要 实现方案 概要 最近在用visual Studio 开发MFC项目时&#xff0c;需要在release版本编译后的exe文件自动追加版本信息。 由于我们用的git工程管理&#xff0c;即需要基于最新的git 提交来打版本。 比如&#xff1a; MFCApplication_V1.0.2_9.exe 由于git 提交信…

pytorch软件封装

封装代码&#xff0c;通过传入文件名&#xff0c;即可输出类别信息 上一章节&#xff0c;我们做了关于动物图像的分类&#xff0c;接下来我们把程序封装&#xff0c;然后进行预测。 单张图片的predict文件 predict.py 按着路径&#xff0c;导入单张图片做预测from torchvis…

【多线程-第四天-自己模拟SDWebImage的下载图片功能-看SDWebImage的Demo Objective-C语言】

一、我们打开之前我们写的异步下载网络图片的项目,把刚刚我们写好的分类拖进来 1.我们这个分类包含哪些文件: 1)HMDownloaderOperation类, 2)HMDownloaderOperationManager类, 3)NSString+Sandbox分类, 4)UIImageView+WebCache分类, 这四个文件吧,把它们拖过来…

电脑提示“找不到mfc140u.dll“的完整解决方案:从原因分析到彻底修复

当你启动某个软件或游戏时&#xff0c;突然遭遇"无法启动程序&#xff0c;因为计算机中丢失mfc140u.dll"的错误提示&#xff0c;这确实令人沮丧。mfc140u.dll是Microsoft Foundation Classes&#xff08;MFC&#xff09;库的重要组成部分&#xff0c;属于Visual C Re…

图像变换方式区别对比(Opencv)

1. 变换示例 import cv2 import matplotlib.pyplot as plotimg cv2.imread(url) img_cut img[100:200, 200:300] img_rsize cv2.resize(img, (50, 50)) (hight,width) img.shape[:2] rotate_matrix cv2.getRotationMatrix2D((hight//2, width//2), 50, 1) img_wa cv2.wa…

图像颜色空间对比(Opencv)

1. 颜色转换 import cv2 import matplotlib.pyplot as plotimg cv2.imread("tmp.jpg") img_r cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img_g cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) img_h cv2.cvtColor(img, cv2.COLOR_BGR2HSV) img_l cv2.cvtColor(img, cv2.C…

每天学一个 Linux 命令(15):man

可访问网站查看,视觉品味拉满:http://www.616vip.cn/15/index.html 每天学一个 Linux 命令(15):man 命令简介 man(Manual)是 Linux 中最核心的命令之一,用于查看命令、系统调用、库函数等的手册文档。它是用户和开发者获取帮助的核心工具,几乎覆盖了系统中的所有功…

必刷算法100题之计算右侧小于当前元素的个数

题目链接 315. 计算右侧小于当前元素的个数 - 力扣&#xff08;LeetCode&#xff09; 题目解析 计算数组里面所有元素右侧比它小的数的个数, 并且组成一个数组,进行返回 算法原理 归并解法(分治) 当前元素的后面, 有多少个比我小(降序) 我们要找到第一比左边小的元素, 这样…

Python依赖注入完全指南:高效解耦、技术深析与实践落地

Python依赖注入完全指南&#xff1a;高效解耦、技术深析与实践落地 摘要 依赖注入&#xff08;DI&#xff09;不仅是一种设计技术&#xff0c;更是一种解耦的艺术。它通过削减模块间的强耦合性&#xff0c;为系统提供了更高的灵活性和可测试性&#xff0c;特别是在 FastAPI 等…

深度学习ResNet模型提取影响特征

大家好&#xff0c;我是带我去滑雪&#xff01; 影像组学作为近年来医学影像分析领域的重要研究方向&#xff0c;致力于通过从医学图像中高通量提取大量定量特征&#xff0c;以辅助疾病诊断、分型、预后评估及治疗反应预测。这些影像特征涵盖了形状、纹理、灰度统计及波形变换等…

【Qt】Qt Creator开发基础:项目创建、界面解析与核心概念入门

&#x1f351;个人主页&#xff1a;Jupiter. &#x1f680; 所属专栏&#xff1a;QT 欢迎大家点赞收藏评论&#x1f60a; 目录 Qt Creator 新建项⽬认识 Qt Creator 界⾯项⽬⽂件解析Qt 编程注意事项认识对象模型&#xff08;对象树&#xff09;Qt 窗⼝坐标体系 Qt Creator 新…

制造业项目管理如何做才能更高效?制造企业如何选择适配的数字化项目管理系统工具?

一、制造企业项目管理过程中面临的痛点有哪些&#xff1f; 制造企业在项目管理过程中面临的痛点通常涉及跨部门协作、资源调配、数据整合、风险控制等多个维度&#xff0c;且与行业特性&#xff08;如离散制造vs流程制造&#xff09;紧密相关。 进度失控多项目资源冲突信息孤…

Python批量处理PDF图片详解(插入、压缩、提取、替换、分页、旋转、删除)

目录 一、概述 二、 使用工具 三、Python 在 PDF 中插入图片 3.1 插入图片到现有PDF 3.2 插入图片到新建PDF 3.3 批量插入多张图片到PDF 四、Python 提取 PDF 图片及其元数据 五、Python 替换 PDF 图片 5.1 使用图片替换图片 5.2 使用文字替换图片 六、Python 实现 …

七种驱动器综合对比——《器件手册--驱动器》

九、驱动器 名称 功能与作用 工作原理 优势 应用 隔离式栅极驱动器 隔离式栅极驱动器用于控制功率晶体管&#xff08;如MOSFET、IGBT、SiC或GaN等&#xff09;的开关&#xff0c;其核心功能是将控制信号从低压侧传输到高压侧的功率器件栅极&#xff0c;同时在输入和输出之…

redis系列--1.redis是什么

国际惯例&#xff0c;想了解一个东西&#xff0c;首先就要看看官方提供了什么。redis的官网是https://redis.io 。以下这段话就是redis的简介了&#xff1a; Redis is an open source (BSD licensed), in-memory data structure store, used as a database, cache, and message…

CSS 过渡与变形:让交互更丝滑

在网页设计中&#xff0c;动效能让用户交互更自然、流畅&#xff0c;提升使用体验。本文将通过 CSS 的 transition&#xff08;过渡&#xff09;和 transform&#xff08;变形&#xff09;属性&#xff0c;带你入门基础动效设计&#xff0c;结合案例演示如何实现颜色渐变、元素…