最全面的SpringMVC教程(六)——WebSocket

这篇具有很好参考价值的文章主要介绍了最全面的SpringMVC教程(六)——WebSocket。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

spring websocket,JavaEE,java,spring,mvc,websocket

本文为 【SpringMVC教程】WebSocket 相关知识介绍,具体将对WebSocket进行简介,并通过实战案例对WebSocket的使用进行详尽介绍~

📌博主主页:小新要变强 的主页
👉Java全栈学习路线可参考:【Java全栈学习路线】最全的Java学习路线及知识清单,Java自学方向指引,内含最全Java全栈学习技术清单~
👉算法刷题路线可参考:算法刷题路线总结与相关资料分享,内含最详尽的算法刷题路线指南及相关资料分享~
👉Java微服务开源项目可参考:企业级Java微服务开源项目(开源框架,用于学习、毕设、公司项目、私活等,减少开发工作,让您只关注业务!)

↩️本文上接:最全面的SpringMVC教程(五)——文件上传与下载


目录

spring websocket,JavaEE,java,spring,mvc,websocket

一、WebSocket 简介

WebSocket 协议提供了一种标准化方式,可通过单个 TCP 连接在客户端和服务器之间建立全双工、双向通信通道。它是与 HTTP 不同的 TCP 协议,但旨在通过 HTTP 工作,使用端口 80 和 443。

WebSocket 交互以 HTTP 请求开始,HTTP请求中包含Upgrade: websocket 时,会切换到 WebSocket 协议。以下示例显示了这样的交互:

GET /spring-websocket-portfolio/portfolio HTTP/1.1
Host: localhost:8080
Upgrade: websocket 
Connection: Upgrade 
Sec-WebSocket-Key: Uc9l9TMkWGbHFD2qnFHltg==
Sec-WebSocket-Protocol: v10.stomp, v11.stomp
Sec-WebSocket-Version: 13
Origin: http://localhost:8080

成功握手后,HTTP 升级请求底层的 TCP 套接字保持打开状态,客户端和服务器都可以继续发送和接收消息。

🍀(1)HTTP 与 WebSocket

尽管 WebSocket 被设计为与 HTTP 兼容并从 HTTP 请求开始,但这两种协议会产生不同的架构和应用程序编程模型。

在 HTTP 和 REST 中,一个应用程序被建模为多个 URL。为了与应用程序交互,客户端访问这些 URL,请求-响应样式。服务器根据 HTTP URL、方法和请求头将请求路由到适当的处理程序。而在 WebSocket中,通常只有一个 URL 用于初始连接。随后,所有应用程序消息都在同一个 TCP 连接上流动。

我们现在有一个需求,就是页面要实时显示当前的库存信息:

短轮询方式:

最简单的一种方式,就是你用JS写个死循环(setInterval),不停的去请求服务器中的库存量是多少,然后刷新到这个页面当中,这其实就是所谓的短轮询。

这种方式有明显的坏处,那就是你很浪费服务器和客户端的资源。客户端还好点,现在PC机配置高了,你不停的请求还不至于把用户的电脑整死,但是服务器就很蛋疼了。如果有1000个人停留在某个商品详情页面,那就是说会有1000个客户端不停的去请求服务器获取库存量,这显然是不合理的。

长轮询方式:

长轮询这个时候就出现了,其实长轮询和短轮询最大的区别是,短轮询去服务端查询的时候,不管库存量有没有变化,服务器就立即返回结果了。而长轮询则不是,在长轮询中,服务器如果检测到库存量没有变化的话,将会把当前请求挂起一段时间(这个时间也叫作超时时间,一般是几十秒)。在这个时间里,服务器会去检测库存量有没有变化,检测到变化就立即返回,否则就一直等到超时为止。

而对于客户端来说,不管是长轮询还是短轮询,客户端的动作都是一样的,就是不停的去请求,不同的是服务端,短轮询情况下服务端每次请求不管有没有变化都会立即返回结果,而长轮询情况下,如果有变化才会立即返回结果,而没有变化的话,则不会再立即给客户端返回结果,直到超时为止。

这样一来,客户端的请求次数将会大量减少(这也就意味着节省了网络流量,毕竟每次发请求,都会占用客户端的上传流量和服务端的下载流量),而且也解决了服务端一直疲于接受请求的窘境。

但是长轮询也是有坏处的,因为把请求挂起同样会导致资源的浪费,假设还是1000个人停留在某个商品详情页面,那就很有可能服务器这边挂着1000个线程,在不停检测库存量,这依然是有问题的。

🍀(2)何时使用 WebSocket

WebSockets 可以使网页具有动态性和交互性。但是,在许多情况下,Ajax 和 HTTP 长轮询的组合可以提供简单有效的解决方案。

例如,新闻、邮件和社交提要需要动态更新,但每隔几分钟更新一次可能也完全没问题。另一方面,协作、游戏和金融应用程序需要更接近实时。

延迟本身并不是决定因素。如果消息量相对较低(例如监控网络故障),HTTP轮询可以提供有效的解决方案。低延迟、高频率和高容量的组合是使用 WebSocket 的最佳案例。

二、WebSocket实战案例

Spring Framework 提供了一个 WebSocket API,我们可以使用它来编写处理 WebSocket 消息的客户端和服务器端应用程序。

🍀(1)引入依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-websocket</artifactId>
    <version>5.2.18.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-messaging</artifactId>
    <version>5.2.18.RELEASE</version>
</dependency>  

🍀(2)创建 WebSocket 服务器需要实现WebSocketHandler接口或者直接扩展TextWebSocketHandlerBinaryWebSocketHandler这两个类,使用起来相对简单一点。以下示例使用TextWebSocketHandler

public class MessageHandler extends TextWebSocketHandler {

    Logger log = LoggerFactory.getLogger(MessageHandler.class);

    //用来保存连接进来session
    private List<WebSocketSession> sessions = new CopyOnWriteArrayList<>();

    /**
     * 关闭连接进入这个方法处理,将session从 list中删除
     */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        sessions.remove(session);
        log.info("{} 连接已经关闭,现从list中删除 ,状态信息{}", session, status);
    }

    /**
     * 三次握手成功,进入这个方法处理,将session 加入list 中
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        sessions.add(session);
        log.info("用户{}连接成功.... ",session);
    }

    /**
     * 处理客户发送的信息,将客户发送的信息转给其他用户
     */
    @Override
    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
        log.info("收到来自客户端的信息: {}",message.getPayload());
        session.sendMessage(new TextMessage("当前时间:"+
                LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss")) +",收到来自客户端的信息!"));
        for(WebSocketSession wss : sessions)
            if(!wss.getId().equals(session.getId())){
                wss.sendMessage(message);
            }
    }
}

🍀(3)有专用的 WebSocket Java 配置和 XML 命名空间支持,用于将前面的 WebSocket 处理程序映射到特定的 URL,如以下示例所示

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer{
    
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(new MessageHandler(), "/message")
        .addInterceptors(new HttpSessionHandshakeInterceptor())
        .setAllowedOrigins("*"); //允许跨域访问
    }
}

以下示例显示了与前面示例等效的 XML 配置:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:websocket="http://www.springframework.org/schema/websocket"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/websocket
        https://www.springframework.org/schema/websocket/spring-websocket.xsd">

    <websocket:handlers>
        <websocket:mapping path="/message" handler="myHandler"/>
        <websocket:handshake-interceptors>
            <bean class="org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor"/>
        </websocket:handshake-interceptors>
    </websocket:handlers>

    <bean id="myHandler" class="com.wang.MessageHandler"/>

</beans>

🍀(4)使用原生js,用来访问websocket

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <title>websocket调试页面</title>
</head>
<body>
<div style="float: left; padding: 20px">
  <strong>location:</strong> <br />
  <input type="text" id="serverUrl" size="35" value="" /> <br />
  <button onclick="connect()">connect</button>
  <button onclick="wsclose()">disConnect</button>
  <br /> <strong>message:</strong> <br /> <input id="txtMsg" type="text" size="50" />
  <br />
  <button onclick="sendEvent()">发送</button>
</div>

<div style="float: left; margin-left: 20px; padding-left: 20px; width: 350px; border-left: solid 1px #cccccc;"> <strong>消息记录</strong>
  <div style="border: solid 1px #999999;border-top-color: #CCCCCC;border-left-color: #CCCCCC; padding: 5px;width: 100%;height: 172px;overflow-y: scroll;" id="echo-log"></div>
  <button onclick="clearLog()" style="position: relative; top: 3px;">清除消息</button>
</div>

</div>
</body>
<!-- 下面是h5原生websocket js写法 -->
<script type="text/javascript">
  let output ;
  let websocket;
  function connect(){ //初始化连接
    output = document.getElementById("echo-log")
    let inputNode = document.getElementById("serverUrl");
    let wsUri = inputNode.value;
    try{
      websocket = new WebSocket(wsUri);
    }catch(ex){
      console.log(ex)
      alert("对不起websocket连接异常")
    }

    connecting();
    window.addEventListener("load", connecting, false);
  }


  function connecting()
  {
    websocket.onopen = function(evt) { onOpen(evt) };
    websocket.onclose = function(evt) { onClose(evt) };
    websocket.onmessage = function(evt) { onMessage(evt) };
    websocket.onerror = function(evt) { onError(evt) };
  }

  function sendEvent(){
    let msg = document.getElementById("txtMsg").value
    doSend(msg);
  }

  //连接上事件
  function onOpen(evt)
  {
    writeToScreen("CONNECTED");
    doSend("WebSocket 已经连接成功!");
  }

  //关闭事件
  function onClose(evt)
  {
    writeToScreen("连接已经断开!");
  }

  //后端推送事件
  function onMessage(evt)
  {
    writeToScreen('<span style="color: blue;">服务器: ' + evt.data+'</span>');
  }

  function onError(evt)
  {
    writeToScreen('<span style="color: red;">异常信息:</span> ' + evt.data);
  }

  function doSend(message)
  {
    writeToScreen("客户端A: " + message);
    websocket.send(message);
  }

  //清除div的内容
  function clearLog(){
    output.innerHTML = "";
  }

  //浏览器主动断开连接
  function wsclose(){
    websocket.close();
  }

  function writeToScreen(message)
  {
    let pre = document.createElement("p");
    pre.innerHTML = message;
    output.appendChild(pre);
  }
</script>
</html>
ws:127.0.0.1:8088/app/message

我们可以看到在websocket的请求中有这样的首部信息:
spring websocket,JavaEE,java,spring,mvc,websocket

而且我们多次发送消息,并没有新的请求产生:

spring websocket,JavaEE,java,spring,mvc,websocket

小知识: 我们经常看到有很多地方使用sockjs完成websocket的建立,原因是一些浏览器中缺少对WebSocket的支持。SockJS是一个浏览器JavaScript库,它提供了一个连贯的、跨浏览器的Javascript API,它在浏览器和web服务器之间创建了一个低延迟、全双工、跨域通信通道。


后记

spring websocket,JavaEE,java,spring,mvc,websocket
👉Java全栈学习路线可参考:【Java全栈学习路线】最全的Java学习路线及知识清单,Java自学方向指引,内含最全Java全栈学习技术清单~
👉算法刷题路线可参考:算法刷题路线总结与相关资料分享,内含最详尽的算法刷题路线指南及相关资料分享~文章来源地址https://www.toymoban.com/news/detail-779768.html

到了这里,关于最全面的SpringMVC教程(六)——WebSocket的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 最全面的Elasticsearch查询调优

    将内存分配给文件系统缓存 ​ Elasticsearch严重依赖文件系统缓存来增加搜索速度,我们要尽量确保至少有一半的可用内存进入文件系统,以便Elasticsearch可以将索引的热点区域保留在屋里内存中。 使用更快的硬件。 创建合适的文档索引映射 ​ 避免连接,nested可以使查询慢几

    2024年02月02日
    浏览(43)
  • 史上最全面的敏感信息收集方案

    介绍一款目录渗透工具: ffuf ffuf Fast web fuzzer written in Go,速度快,自定义性高,非常好用 这里贴出一个我常用的脚本:(这里注意域名或IP后面要加上 /FUZZ ) 语雀( http://yuque.com )是一个企业级协作服务,提供文档、表格、项目管理等协作工具,帮助企业沉淀、整理内部信

    2024年02月05日
    浏览(56)
  • python中最全面的统计分布工具

    scipy 为诸多统计分布函数设计了类,这些类封装了几乎相同的方法,下面先以正态分布为示例,预览一下强大的统计功能。 结果如图所示 其中, norm 为正态分布类, rv 是正态分布对象,然后调用 rv.pdf 函数,绘制出 rv 这个标准正态分布的概率密度函数曲线。 接下来通过 r

    2024年02月05日
    浏览(55)
  • 这可能是最全面的Python入门手册了!

    无论是学习任何一门语言,基础知识一定要扎实,基础功非常的重要,找到一个合适的学习方法和资料会让你少走很多弯路, 你的进步速度也会快很多,无论我们学习的目的是什么,不得不说Python真的是一门值得付出时间去学习的优秀编程语言。 Python对于普通人而言,开启

    2024年02月09日
    浏览(50)
  • 【最新综述】史上最全面的3D语义分割综述(上)

            三维分割是计算机视觉领域的一个基本而具有挑战性的问题,可应用于自动驾驶、机器人、增强现实和医学图像分析。它受到了计算机视觉、图形学和机器学习界的极大关注。传统的三维分割方法基于手工创建的特征和机器学习分类器,缺乏泛化能力。在二维计算

    2024年02月20日
    浏览(50)
  • Django ORM:最全面的数据库处理指南

    深度探讨Django ORM的概念、基础使用、进阶操作以及详细解析在实际使用中如何处理数据库操作。同时,我们还讨论了模型深入理解,如何进行CRUD操作,并且深化理解到数据库迁移等高级主题。为了全面解读Django ORM,我们也讨论了其存在的不足,并对其未来发展进行了展望。

    2024年02月13日
    浏览(52)
  • 最全面的Windows 3.1原版系统镜像下载地址

    微软公司发行的操作系统 Windows 3.1是微软公司发布的Windows 3.x系列的基于MS-DOS的操作系统,于1992年4月6日发布。它是微软Windows家族中最具代表性的产品之一,为Windows 95的成功打下了厚实的基础。 此版本添加了对声音输入输出的基本多媒体的支持和一个CD音频播放器,以及对桌

    2024年04月25日
    浏览(33)
  • 2023 年最全面的 DevOps 工具列表,你用过几个?

    在软件开发领域,DevOps已经成为越来越重要的概念。它强调了开发、测试、运维等各个环节之间的协作和自动化,以提高软件交付的速度和质量。随着时间的推移,DevOps所涉及的工具也不断更新和演进。本文将介绍一个预计在 2023 年最全面的 DevOps 工具列表。 1.1 Jenkins Jenkin

    2024年02月15日
    浏览(42)
  • 这可能是最全面的MySQL面试八股文了

    MySQL是一个关系型数据库,它采用表的形式来存储数据。你可以理解成是Excel表格,既然是表的形式存储数据,就有表结构(行和列)。行代表每一行数据,列代表该行中的每个值。列上的值是有数据类型的,比如:整数、字符串、日期等等。 第一范式1NF 确保数据库表字段的

    2023年04月25日
    浏览(45)
  • 最全面的 JavaScript 基础代码手写指南,读完这篇就够了!

    ​🌈个人主页:前端青山 🔥系列专栏:JavaScript篇 🔖 人终将被年少不可得之物困其一生 依旧 青山 ,本期给大家带来JavaScript篇专栏内容:JavaScript-手写代码基础篇 目录 一、JavaScript 基础 1. 手写 Object.create 2. 手写 instanceof 方法 3. 手写 new 操作符 4. 手写 Promise 5. 手写 Promise.the

    2024年02月05日
    浏览(64)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包