Websocket原理和实践

一、概述

1.websocket是什么?

WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

WebSocket与HTTP协议不同,它使用了独立的端口,并且在建立连接后,不需要在每次数据传输时重新建立连接。这使得它特别适合于实时应用程序,例如聊天,在线游戏和股票交易等,这些应用程序需要高速,双向的数据传输。

WebSocket协议是HTML5标准的一部分,因此它可以在现代浏览器中使用。WebSocket API可以在JavaScript中使用,这样就可以在网页上直接使用WebSocket进行通信。

2.websocket和HTTP对比

WebSocket是一种与HTTP不同的协议。两者都位于OSI模型的应用层,并且都依赖于传输层的TCP协议。 虽然它们不同,但是RFC 6455中规定:it is designed to work over HTTP ports 80 and 443 as well as to support HTTP proxies and intermediaries(WebSocket通过HTTP端口80和443进行工作,并支持HTTP代理和中介),从而使其与HTTP协议兼容。 为了实现兼容性,WebSocket握手使用HTTP Upgrade头[1]从HTTP协议更改为WebSocket协议。

其具体的区别如下:

  1. 通信方式不同: HTTP协议是一种请求-响应协议,客户端发送请求给服务器,服务器返回响应。而WebSocket协议是一种全双工协议,客户端和服务器都可以主动发送消息。
  2. 链接状态不同: HTTP协议是无状态的,每次请求都是独立的。而WebSocket协议是有状态的,建立了连接之后,客户端和服务器之间可以维持长连接。
  3. 数据传输不同: HTTP协议是基于文本的,而WebSocket协议是基于二进制的。
  4. 延迟不同: HTTP协议每次请求都需要建立连接,等待响应,传输数据,释放连接,这整个过程都需要一些时间。而WebSocket协议只需要建立一次连接,之后就可以高效地进行数据传输,所以延迟更小。

3.websocket的优势和劣势

  1. **较少的控制开销。**在连接建立后,服务器和客户端之间交换数据时,用于协议控制的数据包头部相对较小。在不包含扩展的情况下,对于服务器到客户端的内容,此头部大小只有2至10字节(和数据包长度有关);对于客户端到服务器的内容,此头部还需要加上额外的4字节的掩码。相对于HTTP请求每次都要携带完整的头部,此项开销显著减少了。
  2. **更强的实时性。**由于协议是全双工的,所以服务器可以随时主动给客户端下发数据。相对于HTTP请求需要等待客户端发起请求服务端才能响应,延迟明显更少;即使是和Comet等类似的长轮询比较,其也能在短时间内更多次地传递数据。
  3. **保持连接状态。**与HTTP不同的是,Websocket需要先建立连接,这就使得其成为一种有状态的协议,之后通信时可以省略部分状态信息。而HTTP请求可能需要在每个请求都携带状态信息(如身份认证等)。
  4. **更好的二进制支持。**Websocket定义了二进制帧,相对HTTP,可以更轻松地处理二进制内容。
  5. **可以支持扩展。**Websocket定义了扩展,用户可以扩展协议、实现部分自定义的子协议。如部分浏览器支持压缩等。
  6. **更好的压缩效果。**相对于HTTP压缩,Websocket在适当的扩展支持下,可以沿用之前内容的上下文,在传递类似的数据时,可以显著地提高压缩率。

4.websocket应用场景

  1. 在线聊天:实现实时的文本聊天功能。
  2. 直播和视频会议:实现实时的音频和视频传输。
  3. 游戏:实现实时的游戏状态同步和控制。
  4. 实时监控和控制:实现实时的硬件监控和控制。
  5. 数据可视化:实现实时的数据可视化和分析。
  6. 在线协作:实现实时的文档协作和编辑。
  7. 智能家居:实现实时的智能家居控制。
  8. 推送消息:实现实时的推送消息功能

这些场景中,WebSocket的实时通信特性使得WebSocket成为了一种理想的选择。

利用websocket处理在线聊天业务的常见流程:

img

二、原理

1.WebSocket通信原理和机制

HTTP通信方式是请求-响应的单工通信,他的通信模式是一问一答式的,如果需要服务端主动发送消息给客户端,他是做不到的。

Websocket的通信方式是全双工模式,无论客户端还是服务端,都能够自主发起通信。但是WebSocket 是独立的、建立在TCP上的协议。Websocket 通过 HTTP/1.1 协议的101状态码进行握手。为了建立Websocket连接,需要通过浏览器发出请求,之后服务器进行回应,这个过程通常称为“握手”(Handshaking)。握手是通过HTTP协议完成的,但是一旦建立连接后,数据传输将使用WebSocket协议。

img

WebSocket通信的流程如下:

  1. 客户端发送一个HTTP请求,请求的目的是为了要建立一个WebSocket连接。
  2. 服务器收到请求后,给出一个HTTP响应,并升级连接到WebSocket协议。
  3. 客户端和服务器之间建立一个WebSocket连接。
  4. 客户端和服务器之间可以进行双向通信,发送文本和二进制数据。
  5. 当客户端或服务器关闭连接时,WebSocket连接也会关闭。

与 HTTP 通信不同的是,WebSocket 通信是基于TCP的,所以它是一个持久连接。它允许服务器主动发送信息给客户端,而不是等待客户端的请求。这使得 WebSocket 通信成为了实现实时应用的理想选择。

建立了websocket连接,请求的URL会以ws://xx开始,在请求头中会有upgrade:websocket标志,说明该请求是websocket请求。

image-20230817164249524

后续客户端和服务器的消息通信会在同一个回话中展示。

image-20230817164304308

2.Java Websocket API介绍

与HTTP不同,websocket的通信具有生命周期,此生命周期由websocket协议本身支撑,具体实现websocket的语言都需要支持。

  1. 建立连接事件:此事件发生在端点上建立新连接时并且在任何其他事件发生之前

  2. 发送消息事件:此事件接收WebSocket对话中另一端发送的消息。它可以发生在WebSocket端点接收了打开事件之后且在接收关闭事件关闭连接之前的任意时刻

  3. 出现错误事件:此事件在WebSocket连接或者端点发生错误时产生

  4. 连接关闭事件:此事件表示WebSocket端点的连接或者端点目前正在部分的关闭,它可以由参与连接的任意一个端点发出

    img

针对Java来说,websocket的事件都会有对应的方法处理,其主要通过编程式端点服务来处理:

  • @OnOpen:处理建立连接的事件;
	@OnOpenpublic void init(Session session, EndpointConfig config) {}
  • @OnMessage:处理建立连接后收发消息的事件;
	@OnMessagepublic void handleTextMessage(String textMessage) {}
-----------@OnMessagepublic void handleBinaryMessage(byte[] messageData, Session session) {}
  • @OnError:处理消息时候发生错误的处理;
  • @OnClose:处理连接关闭的事件;

三、实践

1.Springboot集成Websocket

通过Springboot集成Websocket有两种实现,分别是基于注解的实现和实现接口的方式,下面分别展开:

1.1.基于注解

添加pom依赖

在pom配置文件中引入spring-boot-starter-websocket

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

在配置类中增加websocket配置Bean

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;/*** @author yangnk* @desc* @date 2023/08/15 00:30**/
@Configuration
public class WebSocketConfig {@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}
}

实现websocketServer

实现一个websocketserver,需要添加@ServerEndpoint(“/websocket/{name}”)注解,该注解需要加在类上,表示请求websocket的url地址,再实现websocket各个声明周期的注解的方法。

声明ConcurrentHashMap<String, WebSocketServer> webSocketMap用来保存websocket连接的session。

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.concurrent.ConcurrentHashMap;/*** @author yangnk* @desc* @date 2023/08/15 00:31**//*** @ServerEndpoint 注解的作用** @ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端,* 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端*/
@Slf4j
@Component
@ServerEndpoint("/websocket/{name}")
public class WebSocketServer {/*** 与某个客户端的连接对话,需要通过它来给客户端发送消息*/private Session session;/*** 标识当前连接客户端的用户名*/private String name;/*** 用于存所有的连接服务的客户端,这个对象存储是安全的* 注意这里的kv,设计的很巧妙,v刚好是本类 WebSocket (用来存放每个客户端对应的MyWebSocket对象)*/private static ConcurrentHashMap<String, WebSocketServer> webSocketMap = new ConcurrentHashMap<>();/*** 连接建立成功调用的方法* session为与某个客户端的连接会话,需要通过它来给客户端发送数据*/@OnOpenpublic void OnOpen(Session session, @PathParam(value = "name") String name){log.info("----------------------------------");this.session = session;this.name = name;// name是用来表示唯一客户端,如果需要指定发送,需要指定发送通过name来区分webSocketMap.put(name,this);log.info("[WebSocket] 连接成功,当前连接人数为:={}", webSocketMap.size());GroupSending(name+" 来了");}/*** 连接关闭调用的方法*/@OnClosepublic void OnClose(){webSocketMap.remove(this.name);log.info("[WebSocket] 退出成功,当前连接人数为:={}", webSocketMap.size());GroupSending(name+" 走了");}/*** 收到客户端消息后调用的方法*/@OnMessagepublic void OnMessage(String message_str){log.info("[WebSocket] 收到消息:{}",message_str);//判断是否需要指定发送,具体规则自定义//message_str的格式 TOUSER:user2;message:aaaaaaaaaaaaaaaaaa;if(message_str.indexOf("TOUSER") == 0){//取出 name和message的值String[] split = message_str.split(";");String[] split1 = split[0].split(":");String[] split2 = split[1].split(":");String name = split1[1];String message = split2[1];//指定发送AppointSending(name,message);}else{//群发GroupSending(message_str);}}/*** 发生错误时调用* @param session* @param error*/@OnErrorpublic void onError(Session session, Throwable error){log.info("发生错误");error.printStackTrace();}/*** 群发* @param message*/public void GroupSending(String message){for (String name : webSocketMap.keySet()){try {webSocketMap.get(name).session.getBasicRemote().sendText(name + ":" + message);}catch (Exception e){e.printStackTrace();}}}/*** 指定发送* @param name* @param message*/public void AppointSending(String name,String message){try {webSocketMap.get(name).session.getBasicRemote().sendText(name + ":" + message);}catch (Exception e){e.printStackTrace();}}
}

控制器Controller调用方法

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
/*** @author yangnk* @desc* @date 2023/08/15 00:32**/@RestController("web_Scoket_system")
@RequestMapping("/api/socket")
public class SystemController {@AutowiredWebSocketServer socketServer;//推送数据接口@ResponseBody@RequestMapping("/socket/push/{name}")public String pushToWeb(@PathVariable String name, @RequestHeader String message) {Map<String,Object> result = new HashMap<>();socketServer.AppointSending(name, message);result.put("name", name);result.put("msg", message);return result.toString();}
}

验证结果

可以在在线websocket测试网站:https://www.bejson.com/httputil/websocket/ 进行验证。

image-20230816114918213

1.2.实现接口

TODO

2.完整代码

https://github.com/yangnk/SpringBoot_Learning/tree/master/SpringBootExample/src/main/java/com/yangnk/webSocketExample

3.常见问题

  1. websocket的默认超时实践为30min,如果需要长时间保持websocket连接,就需要设置保活机制。

TODO

  1. 补充通过实现接口的方式来使用websocket;

参考资料

  1. WebSocket:https://zh.wikipedia.org/zh-cn/WebSocket (官网)
  2. springboot整合websocket最基础入门使用教程详解:https://developer.aliyun.com/article/983651 (websocket demo写的比较详细,具有实操性)
  3. SpringBoot 集成 WebSocket,实现后台向前端推送信息:https://cloud.tencent.com/developer/article/1830998 (websocket demo写的不错)
  4. 一文了解WebSocket及Springboot集成WebSocket:https://developer.aliyun.com/article/1152716 (讲了比较多原理方面的东西)
  5. springboot整合webSocket(看完即入门):https://juejin.cn/post/7139334773356363783
  6. WebSocket使用介绍,看这篇就够了:https://juejin.cn/post/7244407068098560037#heading-6 (websocket的进阶使用)
  7. Java WebSocket 基础 生命周期:https://blog.csdn.net/sakuragio/article/details/98673473 (讲了非常详细的websocket事件和API使用情况)

本文由博客一文多发平台 OpenWrite 发布!

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

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

相关文章

如何利用SFTP如何实现更安全的远程文件传输 ——【内网穿透】

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《高效编程技巧》《cpolar》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 1. 安装openSSH1.1 安装SSH1.2 启动ssh 2. 安装cpolar2.1 配置termux服务 3. 远程SFTP连接配置3.1 查看生成的随机公…

【JavaEE】Spring全家桶实现AOP-统一处理

【JavaEE】AOP&#xff08;2&#xff09; 文章目录 【JavaEE】AOP&#xff08;2&#xff09;1. 统一登录校验处理1.1 自定义拦截器1.2 将自定义拦截器加入到系统配置1.3 测试1.4 对于静态资源的处理1.5 小练习&#xff1a;统一登录拦截处理1.6 拦截器原理1.6.1 执行流程1.6.2 源…

正则中常见的流派及其特性

目前正则表达式主要有两大流派&#xff08;Flavor&#xff09;&#xff1a;POSIX 流派与 PCRE 流派。 1、 POSIX 流派 POSIX 规范定义了正则表达式的两种标准&#xff1a; BRE 标准&#xff08;Basic Regular Expression 基本正则表达式&#xff09;&#xff1b;ERE 标准&am…

MinIO【部署 01】MinIO安装及SpringBoot集成简单测试

MinIO安装及SpringBoot集成测试 1.下载安装1.1 Install the MinIO Server1.2 Launch the MinIO Server1.3 Connect Your Browser to the MinIO Server 2.SpringBoot集成2.1 依赖及配置2.2 代码2.3 测试结果 1.下载安装 下载 https://min.io/download#/linux&#xff1b; 安装文…

AS中回退git历史版本并删除历史提交记录

当您想把某个版本后的代码删除&#xff0c;回滚到指定的版本。可以使用一下的方法。 一、打开AS中git历史提交窗口 二、选择需要回滚的版本选项&#xff0c;右键弹出菜单。选择Reset Current Branch to Here... 三、选择 Hard 选项 soft&#xff1a;将合并的更改应用到当前分支…

nginx会话保持

ip_hash:通过IP保持会话 作用&#xff1a; nginx通过后端服务器地址将请求定向的转发到服务器上。 将客户端的IP地址通过哈希算法加密成一个数值 如果后端有多个服务器&#xff0c;第一次请求到服务器A&#xff0c; 并在务器登录成功&#xff0c;那么再登录B服务器就要重新…

【视觉SLAM入门】5.2. 2D-3D PNP 3D-3D ICP BA非线性优化方法 数学方法SVD DLT

"养气之学&#xff0c;戒之躁急" 1. 3D-2D PNP1.1 代数法1.1.1 DLT(直接线性变换法)1.1.2. P3P 1.2 优化法BA (Bundle Adjustment)法 2. 3D-3D ICP2.1 代数法2.1.1 SVD方法 2.2 优化(BA)法2.2.2 非线性优化方法 前置事项&#xff1a; 1. 3D-2D PNP 该问题描述为&am…

开悟Optimization guide for intermediate tracks

目录 认识模型 参考方案&#xff08;按模块拆解&#xff09; 认识模型 模型控制1名英雄进行镜像1 v 1对战 Actor集群资源为64核CPU 问题特点&#xff1a;单一公平对抗场景&#xff08;同英雄镜像对赛&#xff09;&#xff0c;单位时间样本产能低&#xff0c;累计训练资源相…

libevent源码学习3---事件event

libevent源码学习3—事件event libevent 的基本操作单元是事件。每个事件代表一组条件的集合, 这些条件包括: 文件描述符已经就绪, 可以读取或者写入文件描述符变为就绪状态,可以读取或者写入(仅对于边沿触发 IO)超时事件发生某信号用户触发事件 所有事件具有相似的生命周期。…

CentOS ens160 显示disconnected

使用nmcli device查看网卡状态&#xff0c;显示如图&#xff1a; 检查宿主机系统VMware DHCP Sevice和VMware NAT Sevice服务是否正常运行。 右键点击我的电脑管理按钮&#xff0c;打开计算机管理点击服务

elementui的el-tabs标签页样式修改

一、官网样式&#xff1a; 二、修改样式 1.去掉下划线 效果&#xff1a; 代码: /* 去掉tabs标签栏下的下划线 */ ::v-deep .el-tabs__nav-wrap::after {position: static !important;/* background-color: #fff; */ } 2.改变下划线颜色 效果&#xff1a; 代码&#xff1a;…

JVM——垃圾回收(垃圾回收算法+分代垃圾回收+垃圾回收器)

1.如何判断对象可以回收 1.1引用计数法 只要一个对象被其他对象所引用&#xff0c;就要让该对象的技术加1&#xff0c;某个对象不再引用其&#xff0c;则让它计数减1。当计数变为0时就可以作为垃圾被回收。 有一个弊端叫做循环引用&#xff0c;两个的引用计数都是1&#xff…

STM32 printf函数

printf函数输出流程 用户调用printf()函数到C标准库调用printf函数相关部分&#xff0c;printf函数由编译器提供的stdio.h解析。包含在usart.h文件中。fputc()最终实现输出。用户需要根据最终输出的硬件重新定义该函数&#xff0c;此过程为&#xff1a;printf重定向。 printf的…

测试框架pytest教程(8)失败重试-pytest-rerunfailures

pytest-rerunfailures是一个pytest插件&#xff0c;用于重新运行失败的测试用例。当测试用例在第一次运行时失败&#xff0c;该插件会自动重新运行指定次数的失败用例&#xff0c;以提高稳定性和减少偶发性错误的影响。 要使用pytest-rerunfailures插件&#xff0c;需要按照以…

MySQL双主架构、主从架构

为什么要对数据库做优化&#xff1f; MySQL官方说法&#xff1a; 单表2000万数据就达到瓶颈了。所以为了保证查询效率&#xff0c;要让每张表的大小得到控制。 MySQL主主架构 主数据库都负责增删改查。 比如有1000W的数据&#xff0c;有两个主数据库&#xff0c;就将数据分流给…

学习网络编程No.4【socket编程实战】

引言 北京时间&#xff1a;2023/8/19/23:01&#xff0c;耍了好几天&#xff0c;主要归咎于《我欲封天》这本小说&#xff0c;听了几个晚上之后逐渐入门&#xff0c;在闲暇时间又看了一下&#xff0c;小高潮直接来临&#xff0c;最终在三个昼夜下追完了&#xff0c;哈哈哈&…

在 Pytorch 中使用 TensorBoard

机器学习的训练过程中会产生各类数据&#xff0c;包括 “标量scalar”、“图像image”、“统计图diagram”、“视频video”、“音频audio”、“文本text”、“嵌入Embedding” 等等。为了更好地追踪和分析这些数据&#xff0c;许多可视化工具应运而生&#xff0c;比如之前介绍的…

使用 ChatGPT 创建 PowerPoint 演示文稿

让 ChatGPT 成为您的助手来帮助您编写电子邮件很简单,因为众所周知,它非常能够生成文本。很明显,ChatGPT 无法帮助您做饭。但您可能想知道它是否可以生成文本以外的其他内容。在上一篇文章中,您了解到 ChatGPT 只能通过中间语言为您生成图形。在这篇文章中,您将了解使用中…

Ubuntu 配置国内源

配置国内源 因为众所周知的原因&#xff0c;国外的很多网站在国内是访问不了或者访问极慢的&#xff0c;这其中就包括了Ubuntu的官方源。 所以&#xff0c;想要流畅的使用apt安装应用&#xff0c;就需要配置国内源的镜像。 市面上Ubuntu的国内镜像源非常多&#xff0c;比较有…

PyTorch训练简单的生成对抗网络GAN

文章目录 原理代码结果参考 原理 同时训练两个网络&#xff1a;辨别器Discriminator 和 生成器Generator Generator是 造假者&#xff0c;用来生成假数据。 Discriminator 是警察&#xff0c;尽可能的分辨出来哪些是造假的&#xff0c;哪些是真实的数据。 目的&#xff1a;使…