什么是websocket
-
WebSocket 是一种网络通信协议。RFC6455定义了它的通信标准。
-
WebSocket是HTML5下一种新的协议(websocket协议本质上是一个基于tcp的协议)
-
它实现了浏览器与服务器全双工通信,能更好的节省服务器资源和带宽并达到实时通讯的目的
-
http是一种无状态,无连接,单向的应用层协议,它采用了请求/响应模型,通信请求只能由客户端发起,服务端对请求做出应答处理。这样的弊端显然是很大的,只要服务端状态连续变化,客户端就必须实时响应,都是通过javascript与ajax进行轮询,这样显然是非常麻烦的,同时轮询的效率低,非常的浪费资源(http一直打开,一直重复的连接)。
-
于是就有了websocket,Websocket是一个持久化的协议,它是一种全面双工通讯的网络技术,任意一方都可以建立连接将数据推向另一方,websocket只需要建立一次连接,就可以一直保持
websocket 原理
-
websocket约定了一个通信的规范,通过一个握手的机制,客户端和服务器之间能建立一个类似tcp的连接,从而方便它们之间的通信
-
在websocket出现之前,web交互一般是基于http协议的短连接或者长连接
-
websocket是一种全新的协议,不属于http无状态协议,协议名为"ws"
-
说它是TCP传输,主要体现在建立长连接后,浏览器是可以给服务器发送数据,服务器也可以给浏览器发送请求的。当然它的数据格式并不是自己定义的,是在要传输的数据外层有ws协议规定的外层包的。
websocket与http的关系
-
相同点:
-
都是基于tcp的,都是可靠性传输协议,都是应用层协议
-
不同点:
-
WebSocket是双向通信协议,模拟Socket协议,可以双向发送或接受信息
-
HTTP是单向的
-
WebSocket是需要浏览器和服务器握手进行建立连接的
-
而http是浏览器发起向服务器的连接,服务器预先并不知道这个连接
-
联系
-
WebSocket在建立握手时,数据是通过HTTP传输的。但是建立之后,在真正传输时候是不需要HTTP协议的
-
关系图
实际开发
-
我们有一个需求就是报警数据来了之后在前端报警处理,不使用websocket就只能通过轮询每3秒调用一次接口,在吧查回来的数据进行判断,如果多了就开始调接口继续操作,这样很浪费资源。
-
使用websocket之后,建立连接之后。后端察觉数据变化之后,通过他的send方法通知前端,前端通过onmessage提示调用,可以在里面直接调用查询数据接口继续操作。
-
我们这里是建立连接通过他数据变化后端通知前端,前端有个方法会执行,我们在这个方法里面调用我们查询接口,也可以是后端把这条数据发回来我们处理,根据实际情况而定。
后端代码
我们后端是做了容器化的分布式的,主要代码如下
public class WebSocketConfig {
/**
* 如果使用Springboot默认内置的tomcat容器,则必须注入ServerEndpoint的bean;
* 如果使用外置的web容器,则不需要提供ServerEndpointExporter,下面的注入可以注解掉
*/
@Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
}
public class WebSocketServer {
//与某个客户端的连接会话,需要通过它来给客户端发送数据
private Session session;
private static CopyOnWriteArraySet<WebSocketServer> webSockets = new CopyOnWriteArraySet<>();
//用来存放每个客户端对应的WebSocket对象。
private static Map<String,Session> sessionPool = new HashMap<>();
/**
* 连接成功后调用的方法
* @param session
* @param key
*/
@OnOpen
public void onOpen(Session session, @PathParam("key") String key) throws IOException {
//key为前端传给后端的token
this.session = session;
//从token中获取到userid当做区分websocket客户端的key
Long userId = JwtHelper.getUserId(key);
sessionPool.put(String.valueOf(userId),session);
if (sessionPool.get(String.valueOf(userId))==null){
webSockets.add(this);
}
webSockets.add(this);
System.out.println("webSocket连接成功");
System.out.println("WebSocket有新的连接,连接总数为:"+webSockets.size());
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose() {
webSockets.remove(this);
System.out.println("webSocket连接关闭");
}
/**
* 收到客户端消息后调用的方法,根据业务要求进行处理,这里就简单地将收到的消息直接群发推送出去
* @param message 客户端发送过来的消息
*/
@OnMessage
public void onMessage(String message) {
//心跳检测
int beginIndex = 10;
if ("HeartBeat".equals(message.substring(0,9))){
String token = message.substring(beginIndex);
System.out.println("token1"+token);
if (!"".equals(token)){
System.out.println("token2"+token);
Long userId = JwtHelper.getUserId(token);
sendTextMessage(String.valueOf(userId),"ok");
}
}
System.out.println("WebSocket收到客户端消息:"+message);
}
/**
* 发生错误时的回调函数
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error) {
log.error("发生错误");
error.printStackTrace();
}
/**
* 实现服务器主动推送消息
*/
//单点消息发送
public void sendTextMessage(String key,String message){
Session session = sessionPool.get(key);
if (session!=null){
try {
session.getBasicRemote().sendText(message);
}catch (Exception e){
e.printStackTrace();
}
}
}
}
前端代码
1.在src/utils 建立websocket.js 引入文件,这个websocket是全局的,通过登录,和退出控制,在哪儿页面都可以使用。
// 信息提示
import { Message } from 'element-ui'
// 引入用户id
import { getTenantId, getAccessToken } from '@/utils/auth'
// websocket地址
var url = 'ws://192.168.2.20:48081/websocket/message'
// websocket实例
var ws
// 重连定时器实例
var tt
// websocket重连开关
var websocketswitch = false
// websocket对象
var websocket = {
// websocket建立连接
Init () {
// 判断浏览器是否支持websocket
if (!'WebSocket' in window) {
this.$modal.msgError('您的浏览器不支持WebSocket')
return
}
console.log('getTenantId()', getTenantId())
// 获取用户id
let userId = getTenantId()
// 创建websocket实例
ws = new WebSocket(url + '?userId=' + userId)
// 监听websocket连接
ws.onopen = function () {
Message({
type: 'success',
message: 'websocket连接成功'
})
}
// 监听websocket连接错误信息
ws.onerror = function (e) {
console.log('重连开关', websocketswitch)
console.log('数据传输发生错误', e)
Message({
message: 'websocket传输发生错误',
type: 'error'
})
// 打开重连
reconnect()
}
// 监听websocket接受消息
ws.onmessage = function (e) {
console.log('接收后端消息:' + e.data)
//心跳消息不做处理
if (e.data === 'ok') {
return
}
Message({
message: e.data,
type: 'success'
})
}
},
// websocket连接关闭方法
Close () {
//关闭断开重连机制
websocketswitch = true
ws.close()
},
// websocket发送信息方法
Send (data) {
// 处理发送数据JSON字符串
let msg = JSON.stringify(data)
// 发送消息给后端
ws.send(msg)
},
// 暴露websocket实例
getwebsocket () {
return ws
}
}
// 监听窗口关闭事件,当窗口关闭时-每一个页面关闭都会触发-扩张需求业务
window.onbeforeunload = function () {
console.log('测试方法执行了')
}
// 浏览器刷新重新连接
// 刷新页面后需要重连-并且是在登录之后
if (window.performance.navigation.type == 1 && getAccessToken()) {
console.log('浏览器刷新了')
//刷新后重连
websocket.Init()
}
// 重连方法
function reconnect () {
console.log('重连开关', websocketswitch)
// 判断是否主动关闭连接
if (websocketswitch) {
return
}
// 重连定时器-每次websocket错误方法onerror触发他都会触发
tt && clearTimeout(tt)
tt = setTimeout(function () {
console.log('执行断线重连...')
websocket.Init()
websocketswitch = false
}, 4000)
}
// 暴露对象
export default websocket
2.在登录和退出的时候进行websocket进行建立连接和关闭连接,就是在vuex(src/store/modules/user.js)调用这里方法(详细的Vuex做登录主页文章会有)。
// 引入外部文件
import websocket from '@/utils/websocket'
// 请求
const actions = {
// 登录
async login (ctx, data) {
// 调用mutations里的方法存到state
// 登录成功后创建websocket连接
// res.data 是token
websocket.Init(res.data)
ctx.commit('SET_TOKEN', res.data)
},
// 退出登录
logout (ctx) {
// 主动关闭连接
websocket.onClose()
Message.success('退出成功,请重新登录')
}
}
3.在需要用到websocket使用,我们这里是在首页使用。
// 引入websocket文件
import websocket from '@/utils/websocket'
// 登录成功一进到页面的时候调用
created() {
this.getWebSocket()
},
// getWebSocket()方法
method: {
// websocket 接受消息
getWebSocket() {
// websocket.getWebSocket()这个是websocket里面方法返回ws对象(websocket.js)
let ws = websocket.getWebSocket()
// 通过ws这个对象获取这个websocket里面后端推消息前端执行的方法onmessage。
// 给他赋给我们自己方法 this.websocketonmessage
websocketonmessage(e)就会执行
ws.onmessage = this.websocketonmessage
},
//接收到消息的回调函数
websocketonmessage(e) {
// e后端传回来参数
// console.log(e.data);
// 防止心跳监测,返回来的ok 对方法执行的影响(心跳监测方法也会执行一次)
if (e.data == 'ok') {
//心跳消息不做处理
return
}
// 需要监测的接口 我们查询数据的接口 在进行处理
this.alarmerlist()
},
}
细节:这样登录创建连接之后,后端察觉到数据变化,就通过他的send方法给我们推消息我们前端websocket.js文件的onmessage这个方法会自己调用执行并会接受参数。我们在需要的页面引入websocket使用。把它赋值我们自己写的方法,这样数据一变化我们就会调用一次查询接口,进行处理,大大节约性能(http要用轮询一直调用查询接口)。
总结:
经过这一趟流程下来相信你也对 Vue 中前后端使用WebSocket 有了初步的深刻印象,但在实际开发中我 们遇到的情况肯定是不一样的,所以我们要理解它的原理,万变不离其宗。加油,打工人!文章来源:https://www.toymoban.com/news/detail-821989.html
什么不足的地方请大家指出谢谢 -- 風过无痕文章来源地址https://www.toymoban.com/news/detail-821989.html
到了这里,关于Vue 中前后端使用WebSocket的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!