前言 本文为 【SpringMVC教程】WebSocket 相关知识介绍,具体将对websocket进行简介,并通过实战案例对WEBSocket的使用进行详尽介绍~ 📌博主主页:小新要变强 的主页 👉Jav
本文为 【SpringMVC教程】WebSocket 相关知识介绍,具体将对websocket进行简介,并通过实战案例对WEBSocket的使用进行详尽介绍~
📌博主主页:小新要变强 的主页
👉Java全栈学习路线可参考:【Java全栈学习路线】最全的Java学习路线及知识清单,Java自学方向指引,内含最全Java全栈学习技术清单~
👉算法刷题路线可参考:算法刷题路线总结与相关资料分享,内含最详尽的算法刷题路线指南及相关资料分享~
👉Java微服务开源项目可参考:企业级Java微服务开源项目(开源框架,用于学习、毕设、公司项目、私活等,减少开发工作,让您只关注业务!)
↩️本文上接:最全面的SpringMVC教程(五)——文件上传与下载
WebSocket 协议提供了一种标准化方式,可通过单个 tcp 连接在客户端和服务器之间建立全双工、双向通信通道。它是与 Http 不同的 TCP 协议,但旨在通过 HTTP 工作,使用端口 80 和 443。
WebSocket 交互以 HTTP 请求开始,HTTP请求中包含Upgrade: websocket
时,会切换到 WebSocket 协议。以下示例显示了这样的交互:
GET /spring-websocket-portfolio/portfolio HTTP/1.1Host: localhost:8080Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: Uc9l9TMkWGbHFD2qnFHltg==Sec-WebSocket-Protocol: v10.stomp, v11.stompSec-WebSocket-Version: 13Origin: 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 的最佳案例。
Spring Framework 提供了一个 WebSocket api,我们可以使用它来编写处理 WebSocket 消息的客户端和服务器端应用程序。
🍀(1)引入依赖
<dependency> <groupId>org.springframeworkgroupId> <artifactId>spring-websocketartifactId> <version>5.2.18.RELEASEversion>dependency><dependency> <groupId>org.springframeworkgroupId> <artifactId>spring-messagingartifactId> <version>5.2.18.RELEASEversion>dependency>
🍀(2)创建 WebSocket 服务器需要实现WebSocketHandler
接口或者直接扩展TextWebSocketHandler
或BinaryWebSocketHandler
这两个类,使用起来相对简单一点。以下示例使用TextWebSocketHandler
public class MessageHandler extends TextWebSocketHandler { Logger log = LoggerFactory.getLogger(MessageHandler.class); //用来保存连接进来session private List<WebSocketSession> sessions = new CopyOnWriteArrayList<>(); @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { sessions.remove(session); log.info("{} 连接已经关闭,现从list中删除 ,状态信息{}", session, status); } @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@EnableWebSocketpublic 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('服务器: ' + evt.data+''); } function onError(evt) { writeToScreen('异常信息: ' + 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的请求中有这样的首部信息:
而且我们多次发送消息,并没有新的请求产生:
小知识: 我们经常看到有很多地方使用sockjs完成websocket的建立,原因是一些浏览器中缺少对WebSocket的支持。SockJS是一个浏览器JavaScript库,它提供了一个连贯的、跨浏览器的Javascript API,它在浏览器和web服务器之间创建了一个低延迟、全双工、跨域通信通道。
👉Java全栈学习路线可参考:【Java全栈学习路线】最全的Java学习路线及知识清单,Java自学方向指引,内含最全Java全栈学习技术清单~
👉算法刷题路线可参考:算法刷题路线总结与相关资料分享,内含最详尽的算法刷题路线指南及相关资料分享~
来源地址:https://blog.csdn.net/qq_42146402/article/details/128046067
--结束END--
本文标题: 最全面的SpringMVC教程(六)——WebSocket
本文链接: https://lsjlt.com/news/379549.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
2024-04-01
2024-04-03
2024-04-03
2024-01-21
2024-01-21
2024-01-21
2024-01-21
2023-12-23
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0