百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

初学者必看的WebSocket技术入门篇!

toyiye 2024-07-06 00:14 16 浏览 0 评论


目录

WebSocket入门篇
一、是什么?
二、为什么出现
三、有什么用?
四、如何使用?
	1、WebSocket 客户端
			1)源码示例
			2) WebSocket 事件
  2、WebSocket 服务端
			1)概述
			2)源码示例
			3)注解介绍
五、结束语

WebSocket入门篇

小郭最近接到了一个业务需求,要在页面上展示一些业务数据。业务方要求数据展示要实时更新,因此常规的前端轮询查询方式肯定是行不通了,会导致数据更新有延迟。

小郭想起来以前上大学时接触过的WebSocket技术是可以支持业务方的要求,由于学习时间有点久,需要重新拿起来学习一下了。

本篇文章将WebSocket的基础知识进行了较为全面的总结,希望大家和小郭一样,通过本篇文章的学习可以掌握WebSocket的基本使用。

一、是什么?

WebSocket 是一种网络传输协议,在2008年诞生,2011年成为了国际标准,基于它的WebSocket API也被W3C定为标准,目前所有浏览器都已经支持该协议了。

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

二、为什么出现

互联网早期,很多网站为了实现推送技术,所用的技术都是轮询。轮询是指在特定的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式有一个很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。

而目前比较新的技术是Comet。这种技术虽然可以双向通信,但依然需要反复发出请求。而且在Comet中,普遍采用的长链接,也会消耗服务器资源。

在这种情况下,HTML5定义了WebSocket协议,相对于Http和Comet,只需要建立一次连接,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。

三、有什么用?

WebSocket的特点

  • 更高的实时性

协议是全双工类型的,服务器可以随时主动给客户端推送数据。相对于HTTP短轮询操作需要等待客户端请求服务端才能响应,实时性明显更高。即使和Comet等长轮询来比较,其也能在短时间内传递更多的数据。

  • 更好的数据格式支持

WebSocket支持文本和二进制这两种格式的数据,而且定义了二进制帧,相对比HTTP,可以更容易处理二进制内容。

  • 扩展性强

WebSocket支持用户扩展协议、实现部分自定义的子协议。比如部分浏览器支持的压缩功能。Websocket在适当的扩展支持下,可以沿用之前内容的上下文,在传递类似的数据时,可以大大提高压缩率。

  • 较少的传输开销

在连接创建后,服务端和客户端交换数据时,用于协议元数据控制的数据包长度比较小。相比HTTP请求每次都需要携带完整头部,此项内容传输开销大大减少。

  • 保持连接状态

与HTTP不同的是,Websocket是一种有状态的协议,因为它需要先建立连接,之后在连接关闭之前的每次通信时都可以省略部分状态信息。而HTTP请求每次都需要建立连接(握手), 而且每次请求可能都需要携带状态信息(如身份认证鉴权等)。

  • 建立在 TCP 协议之上,服务器端的实现比较容易
  • 没有同源限制,客户端可以与任意服务器通信
  • 协议标识符是ws(如果加密,则为wss),服务器网址就是 URL
  • 兼容HTTP协议

和HTTP一样,默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。

由于 WebSocket 拥有上述的特点,所以它被广泛地应用在下面的领域:

  • 即时聊天通信
  • 多玩家游戏
  • 在线协同编辑/编辑
  • 实时数据流的拉取与推送
  • 体育/游戏实况
  • 实时地图位置
  • 即时Web应用程序:即时Web应用程序使用一个Web套接字在客户端显示数据,这些数据由后端服务器连续发送。在WebSocket中,数据被连续推送/传输到已经打开的同一连接中,这就是为什么WebSocket更快并提高了应用程序性能的原因。 例如在交易网站或比特币交易中,这是最不稳定的事情,它用于显示价格波动,数据被后端服务器使用Web套接字通道连续推送到客户端。
  • 游戏应用程序:在游戏应用程序中,你可能会注意到,服务器会持续接收数据,而不会刷新用户界面。屏幕上的用户界面会自动刷新,而且不需要建立新的连接,因此在WebSocket游戏应用程序中非常有帮助。
  • 聊天应用程序:聊天应用程序仅使用WebSocket建立一次连接,便能在订阅户之间交换,发布和广播消息。它重复使用相同的WebSocket连接,用于发送和接收消息以及一对一的消息传输。

四、如何使用?

由上图可知:目前主流的 Web 浏览器都支持 WebSocket,所以我们可以在大多数项目中放心地使用它。

Web 浏览器和服务器都必须实现 WebSockets 协议来建立和维护连接。由于 WebSockets 连接长期存在,与典型的 HTTP 连接不同,对服务器有重要的影响。

基于多线程或多进程的服务器无法适用于 WebSockets,因为它旨在打开连接,尽可能快地处理请求,然后关闭连接。任何实际的 WebSockets 服务器端实现都需要一个异步服务器。

下面通过一些案例介绍WebSocket的客户端和服务端基本使用。

1、WebSocket 客户端

在客户端,支持 WebSocket 的 Web 浏览器将通过 WebSocket 对象公开所有必需的客户端功能(主要指支持 Html5 的浏览器)。

1)源码示例

<script type="text/javascript">
    var websocket=null
    if('WebSocket' in window){
        //调用service请求,获取信息
        topic=new WebSocket("ws://localhost:8082/websocket");
    }else{
        alert("该浏览器不支持WebSocket!")
    }

    topic.onopen=function(event){
        console.log("建立连接!");
    }


    topic.onclose=function(event){
        console.log("连接关闭!");
    }

    topic.onmessage=function(event){
        console.log("收到消息!"+event.data);
        document.getElementById("info").textContent +=event.data;
        //弹窗提醒
    }

    topic.onerror=function(){
        alert("websocket发生错误!");
    }

    topic.onbeforeunload=function(){
        topic.close();
    }
</script>

2) WebSocket 事件

以下是 WebSocket 对象的相关事件:

事件

含义

onopen

连接建立时触发

onmessage

客户端接收服务端数据时触发

onerror

通信发生错误时触发

onclose

连接关闭时触发

onbeforeunload

当浏览器窗口关闭或者刷新时触发

2、WebSocket 服务端

1)概述

WebSocket 在服务端的实现非常丰富。Node.js、Java、C++、Python 等多种语言都有自己的解决方案。

本篇仅介绍小郭在学习 WebSocket 过程中接触过的 Java服务端解决方案。

Java 的 web能力 一般都依托于 servlet 容器。

小郭了解的 servlet 容器有:Tomcat、Jetty、Resin。其中 Tomcat7、Jetty7 及以上版本均开始支持 WebSocket(推荐较新的版本,因为随着版本的更迭,对 WebSocket 的支持可能有变更)。

目前流行的Spring 框架对 WebSocket 也提供了支持,主要依赖下面的jar包:

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-websocket</artifactId>
  <version>${spring.version}</version>
</dependency>

基于Spring之上的SpringBoot对WebSocket提供了良好的整合,主要依赖下面的jar包:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-websocket</artifactId>
  <version>${springboot.version}</version>
</dependency>

本篇接下来采用SpringBoot框架来介绍如何整合WebSocket。

2)源码示例

下面记录一下 SpringBoot程序如何整合WebSocket。

为了减少篇幅,demo项目采用前后端不分离的方式,因此也包含了客户端实现代码。

  • a、引入jar包

这里直接创建前后端不分离的web应用程序(实际生产中web前端和后端是分离的工程)

 <!-- 添加Web依赖 -->
 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-web</artifactId>
 </dependency>
 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-thymeleaf</artifactId>
 </dependency>
 <!--websocket-->
 <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
 </dependency>
  • b、配置页面信息

在application.properties中添加页面相关配置

server.port=8082
# Spring thymeleaf
spring.thymeleaf.cache=false
spring.thymeleaf.prefix=classpath:/templates/pages/
spring.thymeleaf.suffix=.html
  • c、配置websocket

WebSocket核心都在这里。

因为WebSocket是类似客户端服务端的形式(采用ws协议),那么这里的WebSocketServer其实就相当于一个ws协议的Controller

直接@ServerEndpoint("/websocket") 、@Component启用即可,然后在里面实现@OnOpen开启连接,@onClose关闭连接,@onMessage接收消息等事件方法。

 @Configuration
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}
package com.gyd.websocket;

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.ServerEndpoint;
import java.util.concurrent.CopyOnWriteArraySet;

@Component
//这个注解用来标记一个类是 WebSocket 的处理器。然后,我们可以在这个类的方法签名上使用一系列注解来表明所修饰的方法是某种事件类型的回调
@ServerEndpoint("/websocket")
public class WebSocketService {
    private Session session;

    //保存连接
    private static CopyOnWriteArraySet<WebSocketService> webSocketService = new CopyOnWriteArraySet<>();

    /**
     * 建立连接
     * @param session
     */
    @OnOpen
    public void opOpen(Session session) {
        this.session = session;
        webSocketService.add(this);
        System.out.println("有新的连接=============》" + webSocketService.size());
    }

    /**
     * 断开连接
     */
    @OnClose
    public void onClose() {
        webSocketService.remove(this);
        System.out.println("断开连接=============》" + webSocketService.size());
    }

    /**
     * 接收客户端消息
     * @param message
     */
    @OnMessage
    public void onMessage(String message) {
        System.out.println("收到客户端消息" + message);
    }

    /**
     * 发送消息到客户端
     * @param message
     */
    public void sendMessage(String message) {
        for (WebSocketService webSocketService2 : webSocketService) {
            System.out.println("广播消息" + message);
            webSocketService2.session.getAsyncRemote().sendText(message);
        }
    }
     /**
     * 传输消息错误触发事件
     * @param error
     */
    @OnError
    public void onError(Throwable error) {

    }
}
  • d、创建接口

controller中只有一个简单的界面跳转操作和模拟造数操作,其他的不需要。

页面访问接口:/show/topic (对应页面名称)

websocket数据模拟接口:/show/createOrder

 package com.gyd.contoller;

import com.gyd.websocket.WebSocketService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.UUID;


/*
 *@Controller:如果当前类所在的包配置了Spring容器包扫描,具有
 *该注解的类,就会作为bean注册到spring容器中,由spring容器创建实例。
 */
@Controller
@RequestMapping("/show/")
public class WebSocketTestController {

    @Autowired
    private WebSocketService webSocketService;

    /**
     * 跳转thymeleaf模板路径
     *
     * @return
     */
    @RequestMapping("/topic")
    public String websocket() {
        return "topic";
    }

    /**
     * 模拟创建订单,发送消息到客户端
     *
     * @return
     */
    @RequestMapping("/createOrder")
    public @ResponseBody String createOrder() {
        webSocketService.sendMessage("你有新的订单,请及时处理========>" + UUID.randomUUID());
        return "新增订单成功!";
    }
}
  • e、编写html页面

新建一个文件,放到 templates目录下面。页面简单使用js代码调用WebSocket。

页面:topic.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
</head>
<body>
<script type="text/javascript">
    var websocket=null
    if('WebSocket' in window){
        //实现化WebSocket对象,指定要连接的服务器地址与端口  建立连接
        topic=new WebSocket("ws://localhost:8082/websocket");
    }else{
        alert("该浏览器不支持WebSocket!")
    }
	//打开事件
    topic.onopen=function(event){
        console.log("建立连接!");
    }
	//关闭事件
    topic.onclose=function(event){
        console.log("连接关闭!");
    }
	//获得消息事件
    topic.onmessage=function(event){
        console.log("收到消息!"+event.data);
        document.getElementById("info").textContent +=event.data;
        //弹窗提醒
    }
	//发生了错误事件
    topic.onerror=function(){
        alert("websocket发生错误!");
    }
	//浏览器窗口关闭或者刷新事件
    topic.onbeforeunload=function(){
        topic.close();
    }
</script>
<h4>您好</h4>
    <span id="info"></span>
</body>
</html>   
  • f、运行测试

先启动应用,然后在浏览器输入127.0.0.1:8082/show/topic 打开topic.html页面,然后调用127.0.0.1:8082/show/createOrder 模拟数据创建,看页面是否收到创建的数据:

这样就简单实现了服务端实时推送数据到客户端的效果啦!

注意: websocket是全双工的,客户端也可以往服务端推送,两者之间只需要建立一次链接即可!

3)注解介绍

@ServerEndpoint:将目前的类定义成一个websocket服务器端,注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端

@OnOpen:当WebSocket建立连接成功后会触发这个注解修饰的方法。

@OnClose:当WebSocket建立的连接断开后会触发这个注解修饰的方法。

@OnMessage:当客户端发送消息到服务端时,会触发这个注解修改的方法。

@OnError:当WebSocket建立连接时出现异常会触发这个注解修饰的方法。

五、结束语

学不完,根本学不完

本文简单介绍了WebSocket的基本概念,并且用代码演示了一个SpringBoot项目如何接入WebSocket。

其实WebSocket可总结的东西还挺多,比如WebSocket扩展,客户端、服务端之间是如何协商、使用扩展的。WebSocket扩展可以给协议本身增加很多能力和想象空间,比如数据的压缩、加密,以及多路复用等。

相关推荐

如何用 coco 数据集训练 Detectron2 模型?

随着最新的Pythorc1.3版本的发布,下一代完全重写了它以前的目标检测框架,新的目标检测框架被称为Detectron2。本教程将通过使用自定义coco数据集训练实例分割模型,帮助你开始使...

CICD联动阿里云容器服务Kubernetes实践之Bamboo篇

本文档以构建一个Java软件项目并部署到阿里云容器服务的Kubernetes集群为例说明如何使用Bamboo在阿里云Kubernetes服务上运行RemoteAgents并在agents上...

Open3D-ML点云语义分割实验【RandLA-Net】

作为点云Open3D-ML实验的一部分,我撰写了文章解释如何使用Tensorflow和PyTorch支持安装此库。为了测试安装,我解释了如何运行一个简单的Python脚本来可视化名为...

清理系统不用第三方工具(系统自带清理软件效果好不?)

清理优化系统一定要借助于优化工具吗?其实,手动优化系统也没有那么神秘,掌握了方法和技巧,系统清理也是一件简单和随心的事。一方面要为每一个可能产生累赘的文件找到清理的方法,另一方面要寻找能够提高工作效率...

【信创】联想开先终端开机不显示grub界面的修改方法

原文链接:【信创】联想开先终端开机不显示grub界面的修改方法...

如意玲珑成熟度再提升,三大发行版支持教程来啦!

前期,我们已分别发布如意玲珑在deepinV23与UOSV20、openEuler24.03发行版的操作指南,本文,我们将为大家详细介绍Ubuntu24.04、Debian12、op...

118种常见的多媒体文件格式(英文简写)

MP4[?mpi?f??]-MPEG-4Part14(MPEG-4第14部分)AVI[e?vi??a?]-AudioVideoInterleave(音视频交错)MOV[m...

密码丢了急上火?码住7种console密码紧急恢复方式!

身为攻城狮的你,...

CSGO丨CS2的cfg指令代码分享(csgo自己的cfg在哪里?config文件位置在哪?)

?...

使用open SSL生成局域网IP地址证书

某些特殊情况下,用户内网访问多可文档管理系统时需要启用SSL传输加密功能,但只有IP,没有域名和证书。这种情况下多可提供了一种免费可行的方式,通过openSSL生成免费证书。此方法生成证书浏览器会提示...

Python中加载配置文件(python怎么加载程序包)

我们在做开发的时候经常要使用配置文件,那么配置文件的加载就需要我们提前考虑,再不使用任何框架的情况下,我们通常会有两种解决办法:完整加载将所有配置信息一次性写入单一配置文件.部分加载将常用配置信息写...

python开发项目,不得不了解的.cfg配置文件

安装软件时,经常会见到后缀为.cfg、.ini的文件,一般我们不用管,只要不删就行。因为这些是程序安装、运行时需要用到的配置文件。但对开发者来说,这种文件是怎么回事就必须搞清了。本文从.cfg文件的创...

瑞芯微RK3568鸿蒙开发板OpenHarmony系统修改cfg文件权限方法

本文适用OpenHarmony开源鸿蒙系统,本次使用的是开源鸿蒙主板,搭载瑞芯微RK3568芯片。深圳触觉智能专注研发生产OpenHarmony开源鸿蒙硬件,包括核心板、开发板、嵌入式主板,工控整机等...

Python9:图像风格迁移-使用阿里的接口

先不多说,直接上结果图。#!/usr/bin/envpython#coding=utf-8importosfromaliyunsdkcore.clientimportAcsClient...

Python带你打造个性化的图片文字识别

我们的目标:从CSV文件读取用户的文件信息,并将文件名称修改为姓名格式的中文名称,进行规范资料整理,从而实现快速对多个文件进行重命名。最终效果:将原来无规律的文件名重命名为以姓名为名称的文件。技术点:...

取消回复欢迎 发表评论:

请填写验证码