WebSocket通信学习笔记

1 简介

WebSocket是一种全双工通信协议,它允许客户端和服务器之间建立持久化的双向连接,从而在不频繁创建HTTP请求的情况下进行实时数据传输。与传统的HTTP协议相比,WebSocket更适合需要实时数据更新的应用场景,如聊天应用、实时股票行情、在线游戏等。
WebSocket 是基于 TCP 的一种新的网络协议。它实现了浏览器与服务器全双工通信——浏览器和服务器只需要完成一次握手,两者之间就可以创建持久性的连接, 并进行双向数据传输。
WebSocket可以通过wss://前缀实现加密通信,类似于HTTPS。由于WebSocket协议本身没有对数据的加密和身份验证机制,因此在使用时通常结合TLS/SSL进行加密,并通过认证机制确保通信的安全性。

1.1 工作原理

1) 握手阶段

WebSocket连接从客户端发起一个标准的HTTP请求开始,但这个请求的头部包含一些额外的字段,表示客户端希望升级到WebSocket协议。这是一个GET请求,并且包含以下几个重要的头部字段:

  • Upgrade: websocket:表示客户端希望将这个连接升级为WebSocket。
  • Connection: Upgrade:与Upgrade字段配合使用,表明连接类型的变化。
  • Sec-WebSocket-Key:一个由客户端生成的随机的Base64编码的key,服务器将用它来生成一个响应密钥以确认握手。
  • Sec-WebSocket-Version:表示WebSocket协议的版本,通常为13。

2) 服务器响应

如果服务器支持WebSocket,它将回应一个HTTP 101状态码,表示协议切换成功,并且在响应头部包含以下字段:

  • Upgrade: websocket:确认协议升级。
  • Connection: Upgrade:与Upgrade字段配合使用。
  • Sec-WebSocket-Accept:服务器根据客户端提供的Sec-WebSocket-Key,加上一个固定的字符串,然后通过SHA-1加密并进行Base64编码后生成的值。这个值用于确认握手的有效性。

握手完成后,连接正式切换为WebSocket协议,客户端和服务器可以开始通过这个持久连接进行双向通信。

3) 数据帧传输

一旦连接建立,客户端和服务器之间的数据传输以帧的形式进行。WebSocket的数据帧由一个小的头部和可变长度的数据负载组成,帧可以携带文本数据(UTF-8编码的字符串)或二进制数据。WebSocket支持以下几种帧类型:

  • 文本帧:用于传输文本数据。
  • 二进制帧:用于传输二进制数据。
  • 关闭帧:用于表示连接关闭。
  • Ping帧:客户端或服务器发送Ping帧以测试连接的连通性。
  • Pong帧:用于响应Ping帧。

4) 连接关闭

WebSocket连接可以由客户端或服务器任意一方发起关闭。关闭连接时,必须发送一个关闭帧,其中可以包含关闭码和关闭原因。收到关闭帧后,另一方应尽快发送自己的关闭帧并关闭连接。

1.2 WebSocket 优缺点分析

1)优点

  • 低延迟:由于WebSocket连接是持久的,消除了频繁创建和关闭连接的开销,延迟较低,非常适合需要实时数据更新的应用。
  • 全双工通信:与HTTP的请求-响应模式不同,WebSocket支持全双工通信,客户端和服务器可以同时发送和接收消息。
  • 节省带宽:WebSocket在建立连接后没有HTTP协议的头部负载,可以节省带宽。

2) 缺点

  • 复杂性:WebSocket的实现和调试可能比传统的HTTP协议要复杂,尤其是在处理连接状态、网络问题和安全性方面。
  • 不适用于所有应用:对于一些不需要实时通信的应用,WebSocket可能显得过于复杂和不必要。

1.3 适用场景

  • 实时聊天应用:WebSocket可以实现低延迟的消息传递,非常适合实时聊天。
  • 实时推送:如新闻、体育比分、金融市场数据等。
  • 多人在线游戏:需要低延迟的数据同步和通信。
  • 协作编辑工具:如在线文档编辑、代码协作等,WebSocket可以实现实时的内容同步。

对比 HTTP:

  • HTTP适用于大多数Web应用和API场景,其简单的请求-响应模型、无状态特性和广泛的工具支持,使其成为Web开发的标准选择。
  • WebSocket则适合需要实时、双向数据传输的应用场景,如聊天、在线游戏和金融数据推送。虽然实现上更为复杂,但其在特定场景下的性能和效率优势是HTTP无法比拟的。

详细比较:

  • **通信模式:**HTTP 是请求-响应模式,单项且无状态的;WebSocket 是全双工通信,双向且持续连接。
  • **连接管理:**HTTP通常是短连接,每次请求都重新建立连接;WebSocket是长连接,握手后保持连接,适合频繁数据交换。
  • **使用场景:**HTTP适用于网页加载、API调用等不需要实时更新的场景;WebSocket适用于实时聊天、在线游戏等需要低延迟、实时数据更新的应用。

HTTP和WebSocket底层都是TCP连接。

2 Demo 实例

1) 创建 SpringBoot 项目

2) 引入 Maven 依赖

<!--导入websocket依赖-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

3) 导入前端页面

<!DOCTYPE HTML>
<html><head><meta charset="UTF-8"><title>WebSocket Demo</title></head><body><input id="text" type="text" /><button onclick="send()">发送消息</button><button onclick="closeWebSocket()">关闭连接</button><div id="message"></div></body><script type="text/javascript">var websocket = null;var clientId = Math.random().toString(36).substr(2);//判断当前浏览器是否支持WebSocketif('WebSocket' in window){//连接WebSocket节点websocket = new WebSocket("ws://localhost:8080/ws/"+clientId);}else{alert('Not support websocket')}//连接发生错误的回调方法websocket.onerror = function(){setMessageInnerHTML("error");};//连接成功建立的回调方法websocket.onopen = function(){setMessageInnerHTML("连接成功");}//接收到消息的回调方法websocket.onmessage = function(event){setMessageInnerHTML(event.data);}//连接关闭的回调方法websocket.onclose = function(){setMessageInnerHTML("close");}//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。window.onbeforeunload = function(){websocket.close();}//将消息显示在网页上function setMessageInnerHTML(innerHTML){document.getElementById('message').innerHTML += innerHTML + '<br/>';}//发送消息function send(){var message = document.getElementById('text').value;websocket.send(message);}//关闭连接function closeWebSocket() {websocket.close();}</script>
</html>

4) 编写后端 websocket 服务类

@Component
@ServerEndpoint("/ws/{sid}")
public class WebSocketServer {// 存放会话对象private static Map<String, Session> sessionMap = new HashMap<>();/*** 连接建立成功调用的方法*/@OnOpenpublic void onOpen(Session session, @PathParam("sid") String sid) {System.out.println("客户端:" + sid + "建立连接");sessionMap.put(sid, session);}/*** 收到客户端消息后调用的方法*/@OnMessagepublic void onMessage(String message, @PathParam("sid") String sid) {System.out.println("收到来自客户端:" + sid + "的信息:" + message);}/*** 连接关闭调用的方法*/@OnClosepublic void onClose(@PathParam("sid") String sid) {System.out.println("连接断开:" + sid);sessionMap.remove(sid);}/*** 群发*/public void sendToAllClient(String message) {Collection<Session> sessions = sessionMap.values();for (Session session : sessions) {try {// 服务器向客户端发送消息session.getBasicRemote().sendText(message);} catch (Exception e) {e.printStackTrace();}}}
}

5)编写 websocket 配置类

这个配置类的主要作用是启用WebSocket支持。当Spring容器启动时,它会自动扫描所有标记了@ServerEndpoint注解的类,并将它们注册为WebSocket端点,允许客户端通过这些端点进行WebSocket通信。 如果你在Spring Boot项目中使用内置的Web容器(如Tomcat、Jetty等),并且使用WebSocket,则需要这个ServerEndpointExporter来将WebSocket端点自动注册到容器中。

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

6) 定时任务模拟后端发送给用户端消息

@Component
public class WebSocketTask {@Autowiredprivate WebSocketServer webSocketServer;/*** 通过websocket每隔五秒向客户发送信息*/@Scheduled(cron = "0/5 * * * * ?")public void sendMessageToClient() {webSocketServer.sendToAllClient("这是来自服务端的消息" + DateTimeFormatter.ofPattern("HH:mm:ss").format(LocalDateTime.now()));}
}

3 结果展示

前端结果:
除了自己能主动给后端发送信息以外,前段每隔五秒还能收到后端发来的信息。
image.png
后端结果:
服务端自动接收前段发来的信息,同时会在定时任务中,每隔五秒给前端发送一条信息。
image.png
代码获取:https://gitee.com/strivezhangp/java-study-log.git 切换到相关分支即可拉取。

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

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

相关文章

IDEA没有SQL语句提示

解决已经在IDEA连接数据库&#xff0c;但是写SQL语句不会提示列名、属性之类的 Mapper 映射没有 SQL 提示 设置中搜索&#xff0c;把方言改成 MySQL SQL Dialects

群晖(Docker Compose)配置 frp 服务

为了方便远程电脑&#xff0c;访问自己电脑上的ComfyUI等服务&#xff0c;配置了 frp 服务。 配置 frp 服务后&#xff0c;发现群晖中的一些服务也可以 stcp 安全的暴露出来。 直接在群晖通过 Docker Compose 方式部署 frps 和 frpc&#xff0c;访问者通过 frpc 安全访问暴露…

【机器学习】支持向量机(SVM)的对偶性、核方法以及核技巧

引言 在SVM中&#xff0c;通过引入拉格朗日乘子&#xff0c;可以将原始问题转化为对偶问题&#xff0c;这种转换具有几个重要的优点&#xff0c;包括简化计算和提供更直观的优化问题的解释 文章目录 引言一、支持向量机&#xff08;SVM&#xff09;的对偶性1.1 原始问题&#x…

pacs图像打不开怎么办 --日常工作总结

先强调一下,我不是专门做图像入库和图像归档,我负责的是临床这边的影像,下面是占在我的业务日常分析总结的哈,(不太专业,勿喷) 我们经常会遇到在打开某个检查的时候,出现黑框,日志定位wado服务取不到图 这种情况一般分为 (1) 工作站,工作组,路由,存储卷配置缺失 ---对应的wad…

如何更改 Mac 上 Java 的默认版本?

优质博文&#xff1a;IT-BLOG-CN 第一次运行/usr/libexec/java_home -V将输出类似以下内容&#xff1a; Matching Java Virtual Machines (3): 1.8.0_05, x86_64: "Java SE 8" /Library/Java/JavaVirtualMachines/jdk1.8.0_05.jdk/Contents/Home 1.6.0_65-b14-4…

安装JKS格式证书

--千金易得 知己难求 本文介绍如何在Tomcat服务器配置JKS格式的SSL证书&#xff0c;具体包括下载和上传证书文件&#xff0c;在Tomcat上配置证书文件和证书密码等参数&#xff0c;以及安装证书后结果的验证。成功配置SSL证书后&#xff0c;您将能够通过HTTPS加密通道安全访问To…

音频检测电路 | 声音传感器模块 | 口哨开关 | Arduino

音频检测电路 | 声音传感器模块 | 口哨开关 | Arduino 案例分析电路设计1. **基本音频检测电路设计**电路结构:2. **灵敏度调节原理**方法:3. **非 MCU 控制的 LED 触发**设计步骤:4. **电路示例**5. **示意图(文本描述)**总结实验方法案例分析 一个硅胶娃娃,挤压或拍打…

Docker原理及实例

目录 一 Docker简介及部署方法 1.1 Docker简介 1.1.1 什么是docker&#xff1f; 1.1.2 docker在企业中的应用场景 1.1.3 docker与虚拟化的对比 1.1.4 docker的优势 2 部署docker 2.1 容器工作方法 2.2 部署第一个容器 2.2.1 配置软件仓库 2.2.2 安装docker-ce并启动服…

【经验分享】CANOPEN协议驱动移植(基于CANfestival源码架构)

【经验分享】CANOPEN协议驱动移植(基于CANfestival源码架构&#xff09; 前言一、CANOPEN整体实现原理二、CANOPEN驱动收发三、Timer定时器四、Object Dictionary对象字典五、CANOPEN应用层接口六、CANOPEN 驱动移植经验总结 前言 本次CANOPEN移植基于CANfestival开源代码&…

SpringBoot中MyBatis使用自定义TypeHandler

&#x1f604; 19年之后由于某些原因断更了三年&#xff0c;23年重新扬帆起航&#xff0c;推出更多优质博文&#xff0c;希望大家多多支持&#xff5e; &#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Mi…

Spring Boot Web开发实践:响应参数的使用方法、IOC、DI和Bean基本介绍

主要介绍了SpringBootWeb响应参数的基本使用和spring框架的控制反转&#xff08;IOC&#xff09;和依赖注入&#xff08;DI&#xff09;以及Bean对象的声明、扫描、注入&#xff01;&#xff01;&#xff01; 目录 前言 响应参数 分层解耦 三层架构 分层解耦 IOC & …

第15届蓝桥杯青少组Scratch初级组省赛真题试卷

第十五届蓝桥杯青少组省赛Scratch初级组真题试卷 题目总数&#xff1a;10 总分数&#xff1a;360 选择题 第 1 题 单选题 Scratch运行以下程序&#xff0c;角色会说( )? A.29 B.31 C.33 D.35 第 2 题 单选题 scratch运行下列哪个程序后&#xff0c;宇航…

RabbitMQ 集群与高可用性

目录 单节点与集群部署 1.1. 单节点部署 1.2. 集群部署 镜像队列 1.定义与工作原理 2. 配置镜像队列 3.应用场景 4. 优缺点 5. Java 示例 分布式部署 1. 分布式部署的主要目标 2. 典型架构设计 3. RabbitMQ 分布式部署的关键技术 4. 部署策略和实践 5. 分布式部署…

解决银河麒麟桌面操作系统V10(特别是2101版本)中无法通过interfaces设置网络

解决银河麒麟桌面操作系统V10&#xff08;特别是2101版本&#xff09;中无法通过interfaces设置网络 1、问题简述2、解决方案1. 尝试删除ppp文件、重启2. 使用NetworkManager &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 1、问题简述 在…

day44——C++对C的扩充

八、C对函数的扩充 8.1 函数重载&#xff08;overload&#xff09; 1> 概念 函数重载就是能够实现"一名多用"&#xff0c;是实现泛型编程的一种 泛型编程&#xff1a;试图以不变的代码&#xff0c;来实现可变的功能 2> 引入背景 程序员在写函数时&#x…

k8s的组件以及安装

目录 概念 k8s的使用场景 k8s的特点 核心组件 master主组件 1.kube-apiserver 2.etcd 3.kube-controller-manager 控制器 4.kube-scheduler node从节点组件 1.kubelet 2.kube-proxy 3.docker 总结 k8s的核心概念 安装k8s 架构 安装步骤 实验&#xff1a;创…

Linux学习笔记(4)----Debian压力测试方法

使用命令行终端压力测试需要两个实用工具&#xff1a;s-tui和stress sudo apt install s-tui stress 安装完成后&#xff0c;在终端中启动 s-tui实用工具&#xff1a; s-tui 执行后如下图&#xff1a; 你可以使用鼠标或键盘箭头键浏览菜单&#xff0c;然后点击“压力选项(Str…

Leetcode Day14排序算法

动态git可以看 :https://leetcode.cn/problems/sort-an-array/solutions/179370/python-shi-xian-de-shi-da-jing-dian-pai-xu-suan-fa/ 选择排序 def selection_sort(nums):n len(nums)for i in range(n):for j in range(i, n):if nums[i] > nums[j]:nums[i], nums[j] …

甲基化组学全流程生信分析教程

甲基化组学全流程分析和可视化教程 读取数据目录下的idat文件的甲基化全流程一键分析 功能简介 甲基化分析模块可以实现甲基化芯片450K, 870kEPIC数据的自动读取&#xff0c;可以读取idat文件&#xff0c;也可以读取beta甲基化矩阵文件甲基化数据的缺失值插值甲基化数据的质…

python测试框架之Pytest

初识Pytest Pytest1.Pytest的特点&#xff1a;2.Pytest的基本使用规则3.pytest安装1&#xff09;使用编译器安装2&#xff09;使用命令安装 4.pytest规则 Pytest Pytest是python的一个第三方单元测试库&#xff0c;它的目的是让单元测试变得容易&#xff0c;并且也能扩展到支持…