WebSocket的原理、作用、常见注解和生命周期的简单介绍,附带SpringBoot示例

文章目录

    • WebSocket是什么
    • WebSocket的原理
    • WebSocket的作用
    • 全双工和半双工
    • 客户端【浏览器】API
    • 服务端 【Java】API
    • WebSocket的生命周期
    • WebSocket的常见注解
    • SpringBoot简单代码示例

WebSocket是什么

WebSocket是一种 通信协议 ,它在 客户端和服务器之间建立了一个双向通信的网络连接 。WebSocket是一种基于TCP连接上进行 全双工通信协议

轮询和WebSocket

上图的左边图是 轮询 的方式进行获取信息,可以看出在 一定时间间隔 的情况下客户端向服务端发起请求,如果有数据则返回数据,没有数据则返回空数据。

右边图则是使用WebSocket方式,在 一次握手 之后,两者就可以 创建持久性的连接 ,如果服务端有数据的话,就可以实时返回数据。

WebSocket的原理

WebSocket的原理图
该图解释了WebSocket连接原理。下面是上图的HTTP协议中的请求和响应数据。

请求数据:

  • GET ws://localhost/chat HTTP/1.1
  • Host: localhost
  • Connection: Upgrade
  • Upgrade: websocket
  • Sec-WebSocket-Version: 13
  • Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==Sec-WebSocket-Extensions: permessage-deflate

响应数据:

  • HTTP/1.1 101 Switching Protocols
  • Upgrade: websocket
  • Connection: Upgrade
  • Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=Sec-WebSocket-Extensions: permessage-deflate

WebSocket的作用

WebSocket允许客户端和服务器在 单个TCP连接上 进行 实时 双向通信,而不是传统的 请求-响应模型 。WebSocket协议允许 任意多的数据包任意方向上 发送,因此可以实现 实时的双向的交互式的 数据传输。

  • 实时数据传输:WebSocket允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要 完成一次握手 ,两者之间就可以直接创建 持久性的连接 ,并进行 双向数据传输
  • 实时通知:WebSocket可以保持一个 长连接,当服务器端有新的消息时,能够实时地推送到客户端。这使得诸如知乎的点赞通知、评论等实时交互功能得以实现,从而提供更好的用户体验。
  • 数据收集:一些 次优级别的数据 ,如行为日志、trace、异常执行栈收集等,可以通过WebSocket通道进行传输。这增加了信息的集中度,并能及时针对用户的行为进行合适的配置推送。

WebSocket通常用于 实时Web应用程序 ,如聊天室、游戏、股票价格跟踪等,这些应用需要 实时交互数据更新

但是在实际使用中仍需注意其安全性。例如,某些工具(如Fiddler、Charles等)能够 捕获WebSocket包 ,但这并不意味着WebSocket本身不安全。在使用WebSocket时,应确保采取适当的安全措施,如加密和认证,以保护数据的传输和存储。

全双工和半双工

全双工(Full Duplex)和半双工(Half Duplex)是 通讯传输 中的两种不同模式,主要区别在于 数据在通信线路上的传输方向和方式

  • 全双工允许数据在 两个方向上同时传输 ,即通信线路可以同时存在 双向信号传输 。这意味着在发送数据的同时,也能接收数据,两者是同步进行的。这种模式类似于我们平时打电话,说话的同时也能听到对方的声音。全双工方式下,通信系统的每一端都设置了 发送器和接收器 ,因此能控制数据同时在两个方向上传送。
  • 半双工模式允许 数据在一个信号载体的两个方向上传输,但不能同时传输 。虽然数据可以沿两个方向传送,但在同一时刻,一个信道只允许单方向传送。若要改变传输方向,需由开关进行切换。半双工通信也被称为 双向交替通信 ,即发送和接收动作是交替进行的,不能同时进行。例如,一条窄窄的马路,同时只能有一辆车通过,当有两辆车对开时,只能一辆先过,另一辆再开。

全双工和半双工的主要区别在于数据传输的同步性和方向性。全双工能实现数据的瞬时同步双向传输,而半双工则只能实现双向交替传输。

客户端【浏览器】API

let  ws  =  new WebSocket(URL);  // WebSocket对象创建
  • 格式:协议://ip地址/访问路径
  • 协议:协议名称为 ws

WebSocket对象相关事件:

事件事件处理程序描述
openws.onopen连接建立时触发
messagews.onmessage客户端接收到服务器发送的数据时触发
closews.onclose连接关闭时触发

WebSocket对象提供的方法:

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

前端客户端使用方式:
websocket

服务端 【Java】API

Tomcat的7.0.5 版本开始支持WebSocket,并且实现了Java WebSocket规范。Java WebSocket 应用由一系列的Endpoint 组成。Endpoint 是一个java对象,代表WebSocket链接的一端,对于服务端,我们可以视为处理具体WebSocket 消息的接口。

我们可以通过两种方式定义 Endpoint

  • 第一种是编程式,即继承类 javax.websocket.Endpoint 并实现其方法。
  • 第二种是注解式,即定义一个POJO,并添加 @ServerEndpoint 相关注解。

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

生命周期方法如下:

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

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

  • 编程式:通过添加 MessageHandler 消息处理器来接收消息
  • 注解式:在定义 Endpoint 时,通过 @OnMessage 注解指定接收消息的方法

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

发送消息则由 RemoteEndpoint 完成, 其实例由 Session 维护。发送消息有2种方式发送消息:

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

服务端使用方式:
在这里插入图片描述

WebSocket的生命周期

该图中右上角应为@OnClose。
在这里插入图片描述

WebSocket的常见注解

JavaWebSocket API 中,常见的注解包括以下几种:

  • @ServerEndpoint :该类级别的注解,用于标记一个类并将其声明为WebSocket服务端点。这意味着此类会被部署为WebSocket服务器并在WebSocket实现的URI空间中可用。
  • @OnOpen :用于标识一个方法,当WebSocket连接建立时被调用。这个方法可以包含连接建立时需要执行的逻辑。
  • @OnClose :用于标识一个方法,当WebSocket连接关闭时被调用。此方法中可以执行与关闭连接相关的操作或资源释放。
  • @OnMessage :用于标识一个方法,在接收到客户端消息时被调用。此方法可以接收不同类型的参数,如String或ByteBuffer,用于处理从客户端接收到的消息。
  • @OnError :用于标识一个方法,在WebSocket通信过程中发生错误时被调用。此方法可以用于处理错误情况,如连接失败、消息解析错误等。

此外,还有一些其他的注解可以用于指定WebSocket的配置,例如:

  • encoders :编码器属性,用于指定将消息从Java对象转换为WebSocket消息格式的编码器。
  • decoders :解码器属性,用于指定将WebSocket消息格式解码为Java对象的解码器。
  • subprotocols :子协议属性,用于指定WebSocket连接支持的子协议。
  • configurator :配置器属性,用于指定WebSocket端点的配置器,可以对端点进行更高级的配置。

以上列出的是基于 Java WebSocket API 的常见注解。如果使用其他编程语言或框架,注解可能会有所不同。

SpringBoot简单代码示例

这是一个简单示例,主要了解在SpringBoot中怎么使用WebSocket。

首先,需要在SpringBoot项目中引入依赖

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

注册WebSocket

@Configuration
public class WebsocketConfig {@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}
}

用来封装http请求的响应数据

@Data
public class Result {private boolean flag;private String message;
}

用于接收登录请求的数据

@Data
public class User {private String userId;private String username;private String password;
}

用于封装浏览器发送给服务端的消息数据

@Data
public class Message {private String toName;private String message;
}

用来封装服务端给浏览器发送的消息数据

@Data
public class ResultMessage {private boolean isSystem;private String fromName;private Object message; //如果是系统消息是数组
}

定义ChatEndpoint 类用于处理WebSocket中数据传输的逻辑,生命周期等功能。

@ServerEndpoint(value = "/chat",configurator = GetHttpSessionConfig.class)
@Component
public class ChatEndpoint {// 线程安全private static final Map<String,Session> onlineUsers = new ConcurrentHashMap<>();private HttpSession httpSession;// 建立websocket连接后,被调用@OnOpenpublic void onOpen(Session session, EndpointConfig config) {//1,将session进行保存this.httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());String user = (String) this.httpSession.getAttribute("user");onlineUsers.put(user,session);//2,广播消息。需要将登陆的所有的用户推送给所有的用户String message = MessageUtils.getMessage(true,null,getFriends());broadcastAllUsers(message);}public Set getFriends() {Set<String> set = onlineUsers.keySet();return set;}private void broadcastAllUsers(String message) {try {//遍历map集合Set<Map.Entry<String, Session>> entries = onlineUsers.entrySet();for (Map.Entry<String, Session> entry : entries) {//获取到所有用户对应的session对象Session session = entry.getValue();//发送消息session.getBasicRemote().sendText(message);}} catch (Exception e) {//记录日志}}// 浏览器发送消息到服务端,该方法被调用@OnMessagepublic void onMessage(String message) {try {//将消息推送给指定的用户Message msg = JSON.parseObject(message, Message.class);//获取 消息接收方的用户名String toName = msg.getToName();String mess = msg.getMessage();//获取消息接收方用户对象的session对象Session session = onlineUsers.get(toName);String user = (String) this.httpSession.getAttribute("user");String msg1 = MessageUtils.getMessage(false, user, mess);session.getBasicRemote().sendText(msg1);} catch (Exception e) {//记录日志}}// 断开 websocket 连接时被调用@OnClosepublic void onClose(Session session) {// 1,从onlineUsers中剔除当前用户的session对象String user = (String) this.httpSession.getAttribute("user");onlineUsers.remove(user);// 2,通知其他所有的用户,当前用户下线了String message = MessageUtils.getMessage(true,null,getFriends());broadcastAllUsers(message);}
}

由于使用user name作为Map的key,所以要实时保存,用于消息的传递标识。如果使用数据库,建议还是使用userId作为key,保证唯一性。

public class GetHttpSessionConfig extends ServerEndpointConfig.Configurator {@Overridepublic void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {//获取HttpSession对象HttpSession httpSession = (HttpSession) request.getHttpSession();//将httpSession对象保存起来sec.getUserProperties().put(HttpSession.class.getName(),httpSession);}
}

controller请求类

@RestController
@RequestMapping("user")
public class UserController {/*** 登陆* @param user 提交的用户数据,包含用户名和密码* @param session* @return*/@PostMapping("/login")public Result login(@RequestBody User user, HttpSession session) {Result result = new Result();if(user != null && "123".equals(user.getPassword())) {result.setFlag(true);//将数据存储到session对象中session.setAttribute("user",user.getUsername());} else {result.setFlag(false);result.setMessage("登陆失败");}return result;}/*** 获取用户名* @param session* @return*/@GetMapping("/getUsername")public String getUsername(HttpSession session) {String username = (String) session.getAttribute("user");return username;}
}

项目目录结构:
在这里插入图片描述

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

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

相关文章

123.Mit6.S081-实验1-Xv6 and Unix utilities

今天我们来进行Mit6.S081实验一的内容。 实验任务 一、启动xv6(难度&#xff1a;Easy) 获取实验室的xv6源代码并切换到util分支。 $ git clone git://g.csail.mit.edu/xv6-labs-2020 Cloning into xv6-labs-2020... ... $ cd xv6-labs-2020 $ git checkout util Branch util …

Go 堆内存分配源码解读

简要介绍 在Go的内存分配中存在几个关键结构&#xff0c;分别是page、mspan、mcache、mcentral、mheap&#xff0c;其中mheap中又包括heapArena&#xff0c;具体这些结构在内存分配中担任什么角色呢&#xff1f; 如下图&#xff0c;可以先看一下整体的结构&#xff1a; mcach…

Linux进程详解二:创建、状态、进程排队

文章目录 进程创建进程状态进程排队 进程创建 pid_t fork(void) 创建一个子进程成功将子进程的pid返回给父进程&#xff0c;0返回给新创建的子进程 fork之后有两个执行分支&#xff08;父和子&#xff09;&#xff0c;fork之后代码共享 bash -> 父 -> 子 创建一个进…

比特币成长的代价

作者&#xff1a;Jeffrey Tucker&#xff0c;作家和总裁。曾就经济、技术、社会哲学和文化等话题广泛发表演讲。编译&#xff1a;秦晋 2017 年之后参与比特币市场的人遇到了与之前的人不同的操作和理想。如今&#xff0c;没有人会太在意之前的事情&#xff0c;说的是 2010-2016…

【全网首发】Mogdb 5.0.6新特性:CM双网卡生产落地方案

在写这篇文章的时候&#xff0c;刚刚加班结束&#xff0c;顺手写了这篇文章。 前言 某大型全国性行业核心系统数据库需要A、B两个物理隔离的双网卡架构方案&#xff0c;已成为行业标准。而最新发布的MogDB 5.0.6的CM新增支持流复制双网段部署&#xff0c;用于网卡级高可用容灾(…

【Linux开发实用篇】备份与恢复

备份 实体机无法做快照&#xff0c;我们可以使用备份和恢复技术 第一种方式 把需要的文件&#xff08;或者分区&#xff09;用TAR打包就好&#xff0c;下次恢复的时候进行解压 第二种方式 使用dump 和 restore 指令&#xff1a; 首先安装这两个指令 yum -y install dump, …

参数传递 的案例

文章目录 12 1 输出一个int类型的数组&#xff0c;要求为&#xff1a; [11,22,33,44,55] package com.zhang.parameter; //有关方法的案例 public class MethodTest3 {public static void main(String[] args) {//输出一个int类型的数组&#xff0c;要求为&#xff1a; [11,…

Android studio顶部‘app‘红叉- Moudle ‘XX.app’ dosen’t exist in project

Android studio顶部app红叉- Moudle ‘XX.app’ dosen’t exist in project 1、现象&#xff1a; 运行老项目或者有时候替换项目中的部分代码&#xff0c;明明没有错但是Android studio就编译报错了。 1.1 Android studio顶部app红叉。 1.2 点击Build没有clear菜单&#xff0…

行为审计软件合集|3款好用的上网行为管控软件推荐

网络技术的快速发展使得企业对于员工上网行为的管控需求也日益增强。 上网行为管控软件不仅可以有效监控员工的网络活动&#xff0c;提高工作效率&#xff0c;还可以防止敏感信息的泄露&#xff0c;保障企业的网络安全。 本文将为您推荐三款好用的上网行为管控软件&#xff1a…

『视觉感官盛宴』3D线上商场全方位展示商品与互动购买体验

随着技术的进步和消费者需求的多样化&#xff0c;3D线上商场作为一种新兴的电子商务平台&#xff0c;正逐渐改变传统的在线购物模式。 一、商品展示革命 在3D线上商场中&#xff0c;商品展示不再局限于静态图片和文字描述。借助先进的3D建模技术&#xff0c;商家能够创建商…

1557.可以到达所有点的最少点数目

题目 给你一个 有向无环图 &#xff0c; n 个节点编号为 0 到 n-1 &#xff0c;以及一个边数组 edges &#xff0c;其中 edges[i] [fromi, toi] 表示一条从点 fromi 到点 toi 的有向边。 找到最小的点集使得从这些点出发能到达图中所有点。题目保证解存在且唯一。 你可以以…

电磁仿真--基本操作-CST-(2)

目录 1. 回顾基操 2. 操作流程 2.1 创建工程 2.2 修改单位 2.3 创建 Shape 2.4 使用拉伸 Extrude 2.5 修改形状 Modify Locally 2.6 导入材料 2.7 材料解释 2.8 材料分配 2.9 查看已分配的材料 2.10 设置频率、背景和边界 2.11 选择 Edge&#xff0c;设置端口 2.…

【网络编程】Java网络编程中的基本概念及实现UDP、TCP客户端服务器程序(万字博文)

系列文章目录 【网络通信基础】网络中的常见基本概念 【网络编程】Java网络编程中的基本概念及实现UDP、TCP客户端服务器程序&#xff08;万字博文&#xff09; 【网络原理】UDP协议的报文结构 及 校验和字段的错误检测机制&#xff08;CRC算法、MD5算法&#xff09; 目录 …

公园景区伴随音乐系统-公园景区数字IP广播伴随音乐系统建设指南

公园景区伴随音乐系统-公园景区数字IP广播伴随音乐系统建设指南 由北京海特伟业任洪卓发布于2024年4月23日 随着“互联网”被提升为国家战略&#xff0c;传统行业与互联网的深度融合正在如火如荼地展开。在这一大背景下&#xff0c;海特伟业紧跟时代步伐&#xff0c;凭借其深厚…

linux常用非基础命令/操作

本篇用于总结蒟蒻博主在使用linux系统的过程中会经常用到但老实记不住的一些非基础命令和操作&#xff0c;方便遗忘时查阅 一&#xff0c;关闭指定端口的进程以释放端口 每个端口都有一个守护进程&#xff0c;kill掉这个守护进程就可以释放端口 ①使用命令【netstat -anp | gre…

[大模型]Qwen-Audio-chat FastApi 部署调用

Qwen-Audio-chat FastApi 部署调用 Qwen-Audio 介绍 Qwen-Audio 是阿里云研发的大规模音频语言模型&#xff08;Large Audio Language Model&#xff09;。Qwen-Audio 可以以多种音频 (包括说话人语音、自然音、音乐、歌声&#xff09;和文本作为输入&#xff0c;并以文本作为…

GoJudge环境部署本地调用云服务器部署go-judge判题机详细部署教程go-judge多语言支持

前言 本文基于go-judge项目搭建&#xff0c;由于go-judge官网项目GitHub - criyle/go-judge: Sandbox Server in REST / gRPC API. Based on Linux container technologies.&#xff0c;资料太少&#xff0c;而且只给了C语言的调用样例&#xff0c;无法知道其他常见语言比如&am…

【Unity学习笔记】第十三 · tag与layer(运行时创建tag和layer)

参考&#xff1a; Unity手册 标签Unity手册 LayersIs it possible to create a tag programmatically?脚本自动添加tag和Layer 注&#xff1a;本文使用Unity版本是2022.3.23f1 转载引用请注明出处&#xff1a;&#x1f517;https://blog.csdn.net/weixin_44013533/article/de…

【JavaScriptthreejs】对于二维平面内的路径进行扩张或缩放

目标 对指定路径 [{x,y,z},{x,y,z},{x,y,z},{x,y,z}.........]沿着边缘向内或向外扩张&#xff0c;达到放大或缩小一定范围的效果&#xff0c;这里我们获取每个点&#xff08;这里是Vector3(x,y,z)&#xff09;,获取前后两个点和当前点的坐标&#xff0c;计算前后两点的向量&a…

Ubuntu 安装 Harbor

一、安装 docker 原文参考传送门 1st 卸载系统自带的 docker 应用 for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done 2nd 设置Docker 的apt源 # Add Dockers official GPG key: sudo…