webSocket原理及其案例

news2025/1/22 13:11:53

常见的消息推送方式

1:轮询方式

浏览器以指定的时间间隔向服务器发出HTTP请求,服务器实现试试返回数据给浏览器

缺点:数据有延时、服务器压力较大。

2:长轮询

浏览器发出ajax(异步)请求,服务器接收端接收到请求后,会阻塞请求直到有数据或者超时才返回。

缺点:

3:SSE服务器发送事件

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

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

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

4:webSocket

基于TCP连接上及逆行全双工通信的协议

全双工:允许数据在两个方向上同时传输。

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

websocket API

客户端【浏览器】API

websocket对象提供的方法

send() 通过websocket对象调用该方法发送数据给服务端

案例

服务端API

Tomcat从7.0.5才支持websocket

Java WebSocket应用由一系列的Endpoint组成,Endpoint是一个Java对象,代表WebSocket链接的一端,对于服务端,我们可以视为处理具体websocket消息的接口

定义Endpoint两种方式:

编程式,继承类javax.websocket.Endpoint并实现其方法。

注解式,定义一个POJO,并添加@ServiceEndpoint相关注解。

Endpoint实例在WebSocket握手时创建,并在客户端于服务端链接过程中有效,最后在链接关闭时结束。在Endpoint接口中明确定义了与其生命周期相关的方法,规范实现者确保生命周期的各个阶段调用示例的相关方法,生命周期方法如下:

方法描述注解
onOpen()当开启一个新的会话时调用,该方法是客户端与服务端握手成功后调用的方法@OnOpen
onClose()当会话关闭时调用@OnClose
onError()当连接过程异常时调用@OnError

两个问题

服务端如何接收客户端发送的数据呢?

1:编程式

通过添加MessageHandler消息处理器来接收消息

2:注解式

在定义Endpoint时,通过@OnMessage注解指定接收消息的方法

服务端如何推送数据给客户端呢?

发送消息则由RemoteEndpoint完成,其实例由Session维护。

发送消息有两种方式

1:通过session.getBasicRemote获取同步消息发送的实例,然后调用其sendXxx()方法发送消息

2:通过session.getAsyncRemote获取异步消息发送实例,然后调用其sendXxx()方法发送消息

例子

代码实现

引入依赖资源

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

编写配置类,扫描添加有@ServerEndpoint注解的Bean

@Configuration
public class FileManageWebSocketConfigMessage {
    /**
     * 注入ServerEndpointExporter,自动注册使用@ServerEndpoint注解的
     * @return
     */
    public ServerEndpointExporter serverEndpointExporter(){
        return new ServerEndpointExporter();
    }
}

编写配置类,获取HttpSession对象

public class GetHttpSessionConfigurator extends ServerEndpointConfig.Configurator {
    public void modifyHandshake(ServerEndpointConfig config, HandshakeRequest request, HandshakeResponse response) {
        //强转
        HttpSession httpSession =(HttpSession) request.getHttpSession();
        //将httpSession对象存储到配置对象中
        config.getUserProperties().put(HttpSession.class.getName(),httpSession);
    }

使用

在@ServerEndpoint注解种引入配置器

@ServerEndpoint(value="/chat",configurator = GetHttpSessionConfigurator.class)

案例代码

webSocket.util

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;

@Component
@ServerEndpoint("/webSocket/{uId}")
@Slf4j
public class WebSocketServerUtil {

    private Session session;
    private static CopyOnWriteArraySet<WebSocketServerUtil > webSocketSet = new CopyOnWriteArraySet<>();
    private static ConcurrentHashMap<Long,WebSocketServerUtil > webSocketMap  = new ConcurrentHashMap<>();
    private Long uId = null;


    @OnOpen
    public void onOpen(Session session, @PathParam("uId") Long uId){
        this.session = session;
        this.uId = uId;
        if(webSocketMap .containsKey(uId)){
            webSocketMap .remove(uId);
            webSocketMap .put(uId,this);
        }else{
            webSocketMap .put(uId,this);
            webSocketSet.add(this);
        }

        log.info("【websocket消息】有新的连接,总数:{}",webSocketMap.size());
    }

    @OnClose
    public void onClose(){
        if(webSocketMap.containsKey(uId)){
            webSocketMap.remove(uId);
            //从set中删除
            webSocketSet.remove(this);
        }
        log.info("【websocket消息】连接断开,总数:{}",webSocketSet.size());
    }

    @OnMessage
    public void onMessage(String message){
        log.info("【websocket消息】收到客户端发来的消息:{}",message);
    }

    public void sendMessage(String message){
        try {
            this.session.getBasicRemote().sendText(message);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
    /**
     * 发送自定义消息
     * */
    public static void sendInfo(String message,Long uId) throws Exception {

        //log.info("发送消息到:"+uId+",报文:"+message);

            if(webSocketMap.containsKey(uId)){
                webSocketMap.get(uId).sendMessage(message);
            }else{
                log.error("用户"+uId+",不在线!");
                throw new Exception("连接已关闭,请刷新页面后重试");
            }

    }
}

调用Util方法

        Long uId = new Long("1");
		Map msgMap = new HashMap();
		msgMap.put("step",1);
		msgMap.put("type",2);
		msgMap.put("msg","hello");
		WebSocketServerUtil.sendInfo(JsonUtil.toJson(msgMap),uId);

前端JS代码

/**
 * 初始化websocket连接
 */
function initWebSocket() {
	let uId = 1;
	var websocket = null;
	if('WebSocket' in window) {
		websocket = new WebSocket("ws://localhost:8009/webSocket"+uId );
	} else {
		alert("该浏览器不支持websocket!");
	}
	websocket.onopen = function(event) {
		console.log("建立连接");
		websocket.send('Hello WebSockets!');
	}
	websocket.onclose = function(event) {
		console.log('连接关闭')
		reconnect(); //尝试重连websocket
	}
	//建立通信后,监听到后端的数据传递
	websocket.onmessage = function(event) {
		let data = JSON.parse(event.data);
		//业务处理....
		if(data.step == 1){
		   alert(data.msg);
		}
	}
	websocket.onerror = function() {
		// notify.warn("websocket通信发生错误!");
		// initWebSocket()
	}
	window.onbeforeunload = function() {
		websocket.close();
	}
// 重连
function reconnect() {
	console.log("正在重连");
	// 进行重连
	setTimeout(function () {
		initWebSocket();
	}, 1000);
}

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

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

相关文章

【HarmonyOS开发】ArkTs使用Http封装

1、鸿蒙中如何进行网络请求 1.1 三方库请求 ohos/axios ohos/retrofit ohos/httpclient 1.2 鸿蒙原生请求 ohos.net.http 2、ArkTs请求模块ohos.net.http 本模块提供HTTP数据请求能力。应用可以通过HTTP发起一个数据请求&#xff0c;支持常见的GET、POST、OPTIONS、HEAD…

阿里云吴结生:云计算是企业实现数智化的阶梯

云布道师 近年来&#xff0c;越来越多人意识到&#xff0c;我们正处在一个数据爆炸式增长的时代。IDC 预测 2027 年全球产生的数据量将达到 291 ZB&#xff0c;与 2022 年相比&#xff0c;增长了近 2 倍。其中 75% 的数据来自企业&#xff0c;每一个现代化的企业都是一家数据公…

Xcode15 iOS 17 Simulator 离线安装,模拟器安装

Xcode 15 安装包的大小相比之前更小&#xff0c;因为除了 macOS 的 Components&#xff0c;其他都需要动态下载安装&#xff0c;否则提示 iOS 17 Simulator Not Installed。 如果不安装对应的运行模拟库 无法真机和模拟器运行&#xff0c;更无法新建项目。但是由于模拟器安装包…

通过for语句遍历一个简单的数组

一、基本思想 创建一个命名为ArrayDemo的类&#xff0c;然后定义一个合适的数组&#xff0c;使用for语句遍历这个数值&#xff0c;然后进行输出。 注意事项&#xff1a; 最好在每个字符之间留下一个空白。 二、基本代码 public class ArrayDemo {public static void main(St…

【沐风老师】3dMax篮球建模方法详解

3dMax足球、排球和篮球建模系列之&#xff1a;篮球建模。对于足球和排球建模&#xff0c;思路是从一个基础模型开始&#xff0c;利用这个基础模型与最终的足球&#xff08;或排球&#xff09;模型的某些相似之处&#xff0c;经过修改编辑&#xff0c;最终完成目标模型的建模。但…

【Amazon 实验②】Amazon WAF功能增强之使用Cloudfront、Lambda@Edge阻挡攻击

文章目录 一、方案介绍二、架构图三、部署方案1. 进入Cloud9 编辑器&#xff0c;新打开一个teminal2. 克隆代码3. 解绑上一个实验中Cloudfront 分配绑定的防火墙4. 使用CDK部署方案5. CDK部署完成6. 关联LambdaEdge函数 四、方案效果 一、方案介绍 采用 LambdaEdge DynamoDB 架…

【Unity】入门

文章目录 概述常用组件各类文件基础知识创建工程工程目录介绍五个窗口面板创建代码和场景 脚本与编程鼠标的输入键盘的输入代码来操作组件获取物体API资源的使用API定时调用与线程向量的基本运算预制体与实例 物理系统与组件案例实操作快捷键来源 Unity已广泛运用到各个领域&am…

使用PE信息查看工具和Dependency Walker工具排查因为库版本不对导致程序启动报错问题

目录 1、问题说明 2、问题分析思路 3、问题分析过程 3.1、使用Dependency Walker打开软件主程序&#xff0c;查看库与库的依赖关系&#xff0c;查看出问题的库 3.2、使用PE工具查看dll库的时间戳 3.3、解决办法 4、最后 VC常用功能开发汇总&#xff08;专栏文章列表&…

C++ map和vector向量使用方法

C map用法 C 中 map 提供的是一种键值对容器&#xff0c;里面的数据都是成对出现的,如下图&#xff1a;每一对中的第一个值称之为关键字(key)&#xff0c;每个关键字只能在 map 中出现一次&#xff1b;第二个称之为该关键字的对应值。 map的使用 需要导入头文件 #include …

金蝶云星空业务对象添加网络互控存储在哪些表

文章目录 金蝶云星空业务对象添加网络互控存储在哪些表【网控操作列表】确定后数据写入《网络控制对象》主表《网络控制对象》多语言 二、【网络互斥列表】数据写入《网络控制互斥对象》 金蝶云星空业务对象添加网络互控存储在哪些表 【网控操作列表】确定后数据写入 《网络控…

ssm基于web的汽车售后服务管理系统的设计与实现论文

摘 要 互联网发展至今&#xff0c;无论是其理论还是技术都已经成熟&#xff0c;而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播&#xff0c;搭配信息管理工具可以很好地为人们提供服务。针对汽车售后服务信息管理混乱&#xff0c;出错率高&#xff0c;信息安全…

【数字通信原理】复习笔记

哈喽&#xff89;hi~ 小伙伴们许久没有更新啦~ 花花经历了漫长的考试周~ 要被累成花干啦。今天来更新《数字通信原理》手写笔记给需要的小伙伴~ &#xff08;注:这是两套笔记&#xff0c;是需要结合来看的哦~&#xff09; 第一套的笔记请结合bilibili:张锦皓的复习课程来哦。 第…

点击筛选框动态增加 多条可输入Table列 以及通过操作数组改造数据

点击筛选框动态增加 多条可输入Table列 以及通过操作数组改造数据 <el-col :span"8" class"tab_group"><el-form-item label"动态筛选"><el-select v-model.trim"ruleForm.flowType" placeholder"请选择" …

【DOM笔记三】节点操作(节点概述、节点层级、添加 / 删除 / 复制节点、DOM基本语法总结!

文章目录 5 节点操作5.1 节点概述5.2 节点层级5.2.1 父子节点5.2.2 兄弟节点 5.3 添加元素5.4 删除节点5.5 复制节点5.6 三种动态创建元素的区别 6 DOM小结 5 节点操作 获取元素的方式&#xff1a; 比较发现&#xff0c;用节点层级关系来获取元素更简单&#xff08;DOM方法相当…

Git常用命令及解释说明

目录 前言1 git config2 git init3 git status4 git add5 git commit6 git reflog7 git log8 git reset结语 前言 Git是一种分布式版本控制系统&#xff0c;广泛用于协作开发和管理项目代码。了解并熟练使用Git的常用命令对于有效地管理项目版本和历史记录至关重要。下面是一些…

Linux一行命令配置jdk环境

使用方法&#xff1a; 压缩包上传 到/opt, 更换命令中对应的jdk包名即可。 注意点&#xff1a;jdk-8u151-linux-x64.tar.gz 解压后名字是jdk1.8.0_151 sudo tar -zxvf jdk-8u151-linux-x64.tar.gz -C /opt && echo export JAVA_HOME/opt/jdk1.8.0_151 | sudo tee -a …

Pixelmator Pro 中文

Pixelmator Pro是一款专为Mac用户设计的强大图像编辑软件。它提供了丰富的功能和直观的界面&#xff0c;使用户可以轻松进行各种图像处理任务。该软件支持各种文件格式&#xff0c;包括JPEG、PNG、GIF、BMP和TIFF等&#xff0c;并可导入Photoshop的psd文件。它提供了丰富的绘画…

【LeetCode刷题笔记】贪心

135.分发糖果 解题思路: 两个数组 + 两次遍历 ,取 最大峰值 ,准备两个数组 L 和 R ,默认填充 1 , 先 从左往右 扫描一遍, 更新 L 数组,如果 右边