基于原生Java WebSocketAPI 快速上手
大家好接下来让我带大家了解和快速上手原生WebSocketAPI
一、WebSocket概述
概述:
WebSocket
是一种网络通信协议,指在客户端与服务器之间的双向实时通信,它在单个TCP连接上提供全双工通信,这使得数据可以在客户端和服务器之间实时交换,而无需频繁地建立和关闭连接,以下是关于WebSocket
的一些关键点:
1、为什么要使用WebSocket?
B/S架构如何交互:
http请求只能从浏览器发起,方向固定,不能从服务器发起
替代方案:
轮询
- 浏览器以指定时间间隔向服务器发出HTTP请求,服务器实时返回数据给浏览器
- 问题:
- 有延时
- 每过几秒钟向服务器发送请求,必然会给服务器造成不小的压力
长轮询
浏览器发出ajax请求(异步请求),服务器接收到请求后,会阻塞请求直到有数据或者超时才返回
我们的HTTP1.1,使用的就是这种方式
- 原来异步请求返回回调函数是这样子返回的!!!我的天呐😲😲😲
问题:
- 因为会阻塞在服务器,所以设置的时间可以比较长一点,对比短轮询对服务器的压力要小一点
SSE(server-sent event):服务器发送事件
SSE在服务器和客户端之间打开一个单向通道
- 这个通道是服务器给浏览器的 服务器 --》浏览器
服务器响应的不再是一次性的数据包,而是text/event-stream类型的数据流信息
服务器有数据变更时将数据流式传输到客户端
二、WebSocket介绍
协议类型
- 全双工(Full Duplex):允许数据在两个方向上同时传输
- 这里的两个方向指的是:浏览器和服务器
- 半双工(Half Duplex):允许数据在两个方向上传输,但是同一个时间段内只允许一个方向上传输
- 这里指的是,只能客户端 -》往服务器发送 或者 服务端 -》 往客户端发送
WebSocket就是一种基于在TCP连接上进行全双工通信的协议
原理解析
工作原理
- 握手
- WebSocket连接始于客户端向服务器发起HTTP请求,如果服务器支持WebSocket,会返回一个包含特定头部的响应,完成协议的升级(upgrade)。这意味着客户端和服务器之间的通信将HTTP转换为WebSocket。
- 持久连接
- 一旦建立连接,客户端和服务器之间的通信就不会再经过HTTP,而是通过WebSocket协议进行。这种连接是持久的,能够保持打开状态,知道乙方主动关闭连接。
- 双向通信
- WebSocket运行客户端和服务器同时发送和接受消息,支持实时数据传输。这意味着服务器可以主动向客户端推送消息,而无需等待客户端的请求
特点
- 低延迟:由于WebSocket使用持久连接,避免了传统HTTP请求中的多次握手过程,因此可以实现更低的延迟和更快的数据传输
- 节省带宽:WebSocket消息头相对较小,特别是再建立连接后,数据传输时的开销更少,这使得它比传统的HTTP请求更省带宽
- 属于实时应用:WebSocket特别适合需要实时更新的应用,如在线聊天、实时协作工具、在线游戏、金融交易平台等。
使用场景
- 在线聊天应用:支持用户之间实时消息传递
- 实时通知:如社交媒体平台上的通知更新
- 在线游戏:实时交互游戏状态和玩家动作
- 股票市场:实时更新股票价格和交易信息
- 协作编辑:如Google Docs等应用程序,允许多个用户同时编辑文档
劣势与优势
- 优势
- 实时性强,适用于需要快熟反应的应用
- 通过持久连接减少连接建立的开销
- 支持双向通信,方便服务器主动推送消息
- 劣势
- 对于某些简单的请求 - 响应场景,WebSocket 可能显的过于复杂
- 需要额外的服务器支持配置,可能增加开发和维护成本
三、WebSocket通信过程
1、浏览器发起http请求,请求建立WebSocket连接
- url的请求头是以ws开头,不是http开头
- 带有两个重要的参数
Upgeade
:websocket
- 升级为
websocket
Connection
:Upgrade
- 连接 : 协议升级
2、服务器响应同意协议更改
- 如果同意更改就会返回一个101的状态码
- 也会带上两个重要的参数
Upgeade
:websocket
- 升级为
websocket
Connection
:Upgrade
- 连接 : 协议升级
3、相互发送数据
4、通信过程总结
- 浏览器发起
http
请求,升级为websocket
- 服务器返回相应码 101 说明升级成功
- 现在可以开始自由沟通了
5、底层原理
WebSocket
协议建立在tcp协议基础上的,所以服务器端也容易实现,不同的语言都有支持tcp
协议是全双工协议,http协议基于它,但是设计成了单向的WebSocket
没有同源限制
- 同源: 同一个端口和同一个ip地址下是同源
- 例子:前后端分离项目一般是在不同的ip端口下运行的,就会出现跨域请求问题
WebSocket
没有这个限制,就算你前端的源和后端的源不一致也不影响请求的发送
四、Java实现WebSocket
注意:这里需要导入websocket-api,我使用的是maven管理,大家也可以去中心仓库下载在导入
jar包下载地址:https://mvnrepository.com/artifact/jakarta.websocket/jakarta.websocket-api
<dependency>
<groupId>jakarta.websocket</groupId>
<artifactId>jakarta.websocket-api</artifactId>
<version>2.0.0</version>
<scope>provided</scope>
</dependency>
实现前端
- 配套html案例
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="./js/jquery-3.7.1.js"></script> <style> *{ padding: 0; margin: 0; } #print-p{ border: 1px solid black; width: 500px; height: 400px; margin: 20px auto; padding: 5px; overflow: auto; } #print-btn-box{ margin: 0 auto; text-align: center; } .print-row{ margin: 5px 0; } #count{ text-align: center; margin-top: 20px; } </style> </head> <body> <div id="count">当前在线人数: <span></span>人 </div> <div id="print-p"> </div> <p id="print-btn-box"> <input type="text" id="send-text"> <button id="send-btn">发送</button> </p> </body> <script> let ws = new WebSocket("ws://169.254.111.241:8080/MultiPersonOnlineChat/ws/hello"); let roucount = 0; ws.onopen = function () { console.log("连接中ing..."); } ws.onmessage = function(event){ let dataObj = JSON.parse(event.data); $("#count span").text(dataObj.count); if(dataObj.message != ""){ $("#print-p").append(`<p class="print-row">${dataObj.sessionId}:${dataObj.message}</p>`); } } ws.onclose = function(){ console.log("连接断开"); } $("#send-btn").click(function(){ ws.send($("#send-text").val()) }) </script> </html>
实际只需要
<script> let ws = new WebSocket("ws://169.254.111.241:8080/MultiPersonOnlineChat/ws/hello"); ws.onopen = function () { console.log("连接中ing..."); } ws.onmessage = function(event){ let dataObj = JSON.parse(event.data); $("#count span").text(dataObj.count); if(dataObj.message != ""){ $("#print-p").append(`<p class="print-row">${dataObj.sessionId}:${dataObj.message}</p>`); } } ws.onclose = function(){ console.log("连接断开"); } $("#send-btn").click(function(){ ws.send($("#send-text").val()) }) </script>
前端相关事件
事件 事件处理程序 描述 open ws.onopen 连接建立时触发事件 message ws.onmessage 客户端接收到服务器发送的数据时触发 close ws.onclose 连接关闭时触发 error ws.onerror 出现报错时触发 相关事件方法
方法名 描述 send() 通过websocket对象调用该方法发送数据给服务端
1、基于Java注解实现WebSocket服务器端
服务终端类需要使用到的注解:
方法 描述 注解 onOpen() 当一个新的会话开启时调用,该方法就是连接成功触发的方法 @OnOpen onClose() 当会话关闭时调用 @OnClose onError() 当连接过程异常时调用 @OnError 当收到客户端消息时调用 @OnMessage 用java注解来监听连接 @ServerEndpoint
Session
对象需要使用的方法
session.getBasicRemoto()
- 获取同步消息发送的实例
- 调用
sendXxx()
方法发送消息session.getAsyncRemote()
- 获取异步消息发送的实例
- 调用
sendXxx()
方法发送消息@ServerEndpoint("/ws/hello") public class HelloWebSocket { static final Map<String,Session> sessionMap = new ConcurrentHashMap<>(); @OnOpen public void onOpen(Session session){ sessionMap.put(session.getId(),session); sendMessage("",session); } @OnMessage public void onMessage(String message,Session session){ sendMessage(message,session); } @OnClose public void onClose(Session session){ sessionMap.remove(session.getId()); } public void sendMessage(String message,Session session){ for (String key : sessionMap.keySet()) { try { sessionMap.get(key).getBasicRemote().sendText("{\"sessionId\":\""+session.getId()+"\",\"message\":\""+message+"\",\"count\":\""+sessionMap.size()+"\"}"); } catch (IOException e) { throw new RuntimeException(e); } } } }
结束:看到这里大家应该对WebSocket有一定的了解,并且能够运行,后续如果大家还想要学习如何使用原生Java的WebSocket API来实现私聊,可以留下评论!!
+session.getId()+“”,“message”:“”+message+“”,“count”:“”+sessionMap.size()+“”}");
} catch (IOException e) { throw new RuntimeException(e); } } }
}