WebSocket 是一种网络通信协议,很多高级功能都需要它。
本文介绍 WebSocket 使用方法。
一、为什么需要 WebSocket?
初次接触 WebSocket 的人,都会问同样的问题:我们已经有了 HTTP 协议,为什么还需要另一个协议?它能带来什么好处?
答案很简单,因为 HTTP 协议有一个缺陷:通信只能由客户端发起。
举例来说,我们想了解今天的天气,只能是客户端向服务器发出请求,服务器返回查询结果。HTTP 协议做不到服务器主动向客户端推送信息。
二、vue项目中WebSocket方法封装
1.代码封装
export class Websocket {
messageSubject= null // subject对象,用于发送事件
url= '' // 默认请求的url
webSocket // websocket对象
connectSuccess = false // websocket 连接成功
period = 60 * 1000 * 3 // 10分钟检查一次
serverTimeoutSubscription = null // 定时检测连接对象
reconnectFlag = false // 重连
reconnectPeriod = 5 * 1000 // 重连失败,则5秒钟重连一次
reconnectSubscription = null // 重连订阅对象
runTimeSubscription // 记录运行连接subscription
runTimePeriod = 60 * 1000 // 记录运行连接时间
isReconnect = true //是否重连 默认重连 当页面卸载时 不要重连
constructor() {
this.messageSubject = {}
console.log('开始心跳监测')
// 进入程序就进行心跳检测,避免出现开始就连接中断,后续不重连
this.heartCheckStart()
this.calcRunTime()
}
//发送消息
sendMessage(msg) {
this.webSocket.send(msg)
}
//创建新连接
connect(url) {
if (!!url) {
this.url = url
}
this.createWebSocket()
}
//创建连接
createWebSocket() {
// 如果没有建立过连接,才建立连接并且添加时间监听
this.webSocket = new WebSocket(this.url)
// 建立连接成功
this.webSocket.onopen = e => this.onOpen(e)
// 接收到消息
this.webSocket.onmessage = e => this.onMessage(e)
// 连接关闭
this.webSocket.onclose = e => this.onClose(e)
// 异常
this.webSocket.onerror = e => this.onError(e)
}
//连接打开
onOpen(e) {
console.log('websocket 已连接')
// 设置连接成功
this.connectSuccess = true
// 如果是重连中
if (this.reconnectFlag) {
// 1.停止重连
this.stopReconnect()
// 2.重新开启心跳
this.heartCheckStart()
// 3.重新开始计算运行时间
this.calcRunTime()
}
}
// 接收到消息
onMessage(event) {
console.log('接收到的消息', event.data)
// 将接受到的消息发布出去
const message = JSON.parse(event.data)
console.log('接收到消息时间', new Date().getTime())
this.messageSubject = message
}
//连接关闭
onClose(e) {
console.log('连接关闭', e)
this.connectSuccess = false
this.webSocket.close()
// 关闭时开始重连
this.reconnect()
this.stopRunTime()
// throw new Error('webSocket connection closed:)');
}
//连接异常
onError(e) {
// 出现异常时一定会进onClose,所以只在onClose做一次重连动作
console.log('连接异常', e)
this.connectSuccess = false
// throw new Error('webSocket connection error:)');
}
//开始重新连接
reconnect() {
if (!this.isReconnect) {
//页面卸载时 不要重连
return
}
// 如果已重连,则直接return,避免重复连接
if (this.connectSuccess) {
this.stopReconnect()
console.log('已经连接成功,停止重连')
return
}
// 如果正在连接中,则直接return,避免产生多个轮训事件
if (this.reconnectFlag) {
console.log('正在重连,直接返回')
return
}
// 开始重连
this.reconnectFlag = true
// 如果没能成功连接,则定时重连
let n = 0;
this.reconnectSubscription = setTimeout(() => {
console.log(`重连${n++}次`)
const url = this.url
// 重新连接
this.connect(url)
},this.reconnectPeriod)
}
//停止重连
stopReconnect() {
// 连接标识置为false
this.reconnectFlag = false
// 取消订阅
if (typeof this.reconnectSubscription !== 'undefined' && this.reconnectSubscription != null) {
clearTimeout(this.reconnectSubscription)
}
}
//开始心跳监测
heartCheckStart() {
this.serverTimeoutSubscription = setTimeout(() => {
// 保持连接状态,重置下
if (this.webSocket != null && this.webSocket.readyState === 1) {
console.log('连接状态,发送消息保持连接')
} else {
// 停止心跳
this.heartCheckStop()
// 开始重连
this.reconnect()
console.log('连接已断开,重新连接')
}
},this.period)
}
//停止心跳监测
heartCheckStop() {
// 取消订阅停止心跳
if (typeof this.serverTimeoutSubscription !== 'undefined' && this.serverTimeoutSubscription != null) {
clearTimeout(this.serverTimeoutSubscription)
}
}
//开始计算运行时间
calcRunTime() {
let i = 0;
this.runTimeSubscription = setTimeout(() => {
console.log('运行时间', `${i++}分钟`)
}, this.runTimePeriod);
}
//停止计算运行时间
stopRunTime() {
if (typeof this.runTimeSubscription !== 'undefined' && this.runTimeSubscription !== null) {
clearTimeout(this.runTimeSubscription)
}
}
//关闭连接
close() {
console.log('连接关闭')
this.isReconnect = false
this.webSocket.close()
this.heartCheckStop()
this.stopRunTime()
}
}
2.方法调用
...
<script>
import { Websocket } from '@/utils/websocket'
export default {
name: 'Websocket',
data() {
return {
list: [],
websocket: null,
}
},
watch: {
'websocket.messageSubject': function(newValue){
if(newValue.message) {
this.list.unshift(newValue.message)
}
}
},
created(){
this.websocket = new Websocket();
let host = location.host
let url = ''
if(location.protocol == 'https:') {
url = `wss://${host}/...`
}else {
url = `ws://${host}/...`
}
this.websocket.connect(url)
},
methods: {},
destroyed() {
this.websocket.close()
this.websocket = null
},
}
</script>
...
三、Angular## 项目中WebSocket方法封装
1.代码封装
import { Injectable } from '@angular/core'
import { interval, Subject } from 'rxjs'
@Injectable({
providedIn: 'root'
})
export class SecurityWebsocketService {
messageSubject: Subject<any> // subject对象,用于发送事件
private url: string // 默认请求的url
private webSocket: WebSocket // websocket对象
connectSuccess = false // websocket 连接成功
period = 60 * 1000 * 3 // 10分钟检查一次
serverTimeoutSubscription: any = null // 定时检测连接对象
reconnectFlag = false // 重连
reconnectPeriod = 5 * 1000 // 重连失败,则5秒钟重连一次
reconnectSubscription: any = null // 重连订阅对象
runTimeSubscription: any // 记录运行连接subscription
runTimePeriod = 60 * 10000 // 记录运行连接时间
isReconnect: boolean = true //是否重连 默认重连 当页面卸载时 不要重连
constructor() {
this.messageSubject = new Subject()
console.log('开始心跳监测')
// 进入程序就进行心跳检测,避免出现开始就连接中断,后续不重连
this.heartCheckStart()
this.calcRunTime()
}
//发送消息
sendMessage(msg: string) {
this.webSocket.send(msg)
}
//创建新连接
connect(url: string) {
if (!!url) {
this.url = url
}
this.createWebSocket()
}
//创建连接
createWebSocket() {
// 如果没有建立过连接,才建立连接并且添加时间监听
this.webSocket = new WebSocket(this.url)
// 建立连接成功
this.webSocket.onopen = e => this.onOpen(e)
// 接收到消息
this.webSocket.onmessage = e => this.onMessage(e)
// 连接关闭
this.webSocket.onclose = e => this.onClose(e)
// 异常
this.webSocket.onerror = e => this.onError(e)
}
//连接打开
onOpen(e: Event) {
console.log('websocket 已连接')
// 设置连接成功
this.connectSuccess = true
// 如果是重连中
if (this.reconnectFlag) {
// 1.停止重连
this.stopReconnect()
// 2.重新开启心跳
this.heartCheckStart()
// 3.重新开始计算运行时间
this.calcRunTime()
}
}
// 接收到消息
onMessage(event: MessageEvent<any>) {
console.log('接收到的消息', event.data)
// 将接受到的消息发布出去
const message = JSON.parse(event.data)
console.log('接收到消息时间', new Date().getTime())
this.messageSubject.next(message)
}
//连接关闭
onClose(e: CloseEvent) {
console.log('连接关闭', e)
this.connectSuccess = false
this.webSocket.close()
// 关闭时开始重连
this.reconnect()
this.stopRunTime()
// throw new Error('webSocket connection closed:)');
}
//连接异常
private onError(e: Event) {
// 出现异常时一定会进onClose,所以只在onClose做一次重连动作
console.log('连接异常', e)
this.connectSuccess = false
// throw new Error('webSocket connection error:)');
}
//开始重新连接
reconnect() {
if (!this.isReconnect) {
//页面卸载时 不要重连
return
}
// 如果已重连,则直接return,避免重复连接
if (this.connectSuccess) {
this.stopReconnect()
console.log('已经连接成功,停止重连')
return
}
// 如果正在连接中,则直接return,避免产生多个轮训事件
if (this.reconnectFlag) {
console.log('正在重连,直接返回')
return
}
// 开始重连
this.reconnectFlag = true
// 如果没能成功连接,则定时重连
this.reconnectSubscription = interval(this.reconnectPeriod).subscribe(async val => {
console.log(`重连:${val}次`)
const url = this.url
// 重新连接
this.connect(url)
})
}
//停止重连
stopReconnect() {
// 连接标识置为false
this.reconnectFlag = false
// 取消订阅
if (typeof this.reconnectSubscription !== 'undefined' && this.reconnectSubscription != null) {
this.reconnectSubscription.unsubscribe()
}
}
//开始心跳监测
heartCheckStart() {
this.serverTimeoutSubscription = interval(this.period).subscribe(val => {
// 保持连接状态,重置下
if (this.webSocket != null && this.webSocket.readyState === 1) {
console.log(val, '连接状态,发送消息保持连接')
} else {
// 停止心跳
this.heartCheckStop()
// 开始重连
this.reconnect()
console.log('连接已断开,重新连接')
}
})
}
//停止心跳监测
heartCheckStop() {
// 取消订阅停止心跳
if (typeof this.serverTimeoutSubscription !== 'undefined' && this.serverTimeoutSubscription != null) {
this.serverTimeoutSubscription.unsubscribe()
}
}
//开始计算运行时间
calcRunTime() {
this.runTimeSubscription = interval(this.runTimePeriod).subscribe(period => {
console.log('运行时间', `${period}分钟`)
})
}
//停止计算运行时间
stopRunTime() {
if (typeof this.runTimeSubscription !== 'undefined' && this.runTimeSubscription !== null) {
this.runTimeSubscription.unsubscribe()
}
}
//关闭连接
close() {
console.log('连接关闭')
this.isReconnect = false
this.webSocket && this.webSocket.close()
this.heartCheckStop()
this.stopRunTime()
}
}
2.方法调用
import { Component, OnInit } from '@angular/core'
import { WebsocketService } from './websocket.service'
@Component({
selector: 'websocket',
templateUrl: './websocket.component.html'
})
export class WebsocketComponent implements OnInit {
data: any[] = []
constructor(private websocket: WebsocketService) {}
ngOnInit(): void {
let host = location.host
let protocol = location.protocol == 'https:' ? 'wss' : 'ws'
let url = `${protocol}://${host}/...`
this.websocket.connect(url)
this.websocket.messageSubject.subscribe((data: any) => {
try {
console.log('websocket返回数据', data)
} catch (e) {
console.error(e)
}
})
}
ngOnDestroy() {
this.websocket.close()
this.websocket = null
}
}