基于webSocket通信的库主要有 socket.ioSockJS,这次用的是 SockJS。

  这里我们使用sockjs-clientstomjs这两个模块,要实现webSocket通信,需要后台配合,也使用相应的模块。

1、sockjs-client

  sockjs-client是从SockJS中分离出来的用于客户端使用的通信模块,所以我们就直接来看看SockJS。SockJS是一个浏览器的JavaScript库,它提供了一个类似于网络的对象,SockJS提供了一个连贯的、跨浏览器的JavaScriptAPI,它在浏览器和Web服务器之间创建了一个低延迟、全双工、跨域通信通道。你可能会问,我为什么不直接用原生的WebSocket而要使用SockJS呢?这得益于SockJS的一大特性,一些浏览器中缺少对WebSocket的支持,因此回退选项是必要的,而Spring框架提供了基于SockJS协议的透明的回退选项。SockJS提供了浏览器兼容性,优先使用原生的WebSocket,如果某个浏览器不支持WebSocket,SockJS会自动降级为轮询。

2、stomjs

  STOMP(Simple Text-Orientated Messaging Protocol) 面向消息的简单文本协议,WebSocket是一个消息架构,不强制使用任何特定的消息协议,它依赖于应用层解释消息的含义。与HTTP不同,WebSocket是处在TCP上非常薄的一层,会将字节流转化为文本/二进制消息,因此,对于实际应用来说,WebSocket的通信形式层级过低,因此可以在 WebSocket 之上使用STOMP协议,来为浏览器 和 server间的通信增加适当的消息语义。

  STOMP与WebSocket 的关系:

  HTTP协议解决了web浏览器发起请求以及web服务器响应请求的细节,假设HTTP协议不存在,只能使用TCP套接字来编写web应用,你可能认为这是一件疯狂的事情

  直接使用WebSocket(SockJS)就很类似于使用TCP套接字来编写web应用,因为没有高层协议,就需要我们定义应用间发送消息的语义,还需要确保连接的两端都能遵循这些语义;

  同HTTP在TCP套接字上添加请求-响应模型层一样,STOMP在WebSocket之上提供了一个基于帧的线路格式层,用来定义消息语义.

3、代码实现:

  先安装 sockjs-client 和 stompjs

npm install sockjs-client
npm install stompjs

  简单代码:

<script>
import SockJS from 'sockjs-client'
import Stomp from 'stompjs'
import { getLiveUrlApi, getLiveEventDetailApi } from '@/apis'
import { mapGetters } from 'vuex'
import { picklist, isWinxin, isMobile, initWxShare } from '@/utils'
import Emoji from './emoji'
export default {
data () {
return {
stompClient: '',
timer: '',
player: null,
pkl: picklist,
danmukuList: [],
barrageInfo: {
text: ''
},
total: ,
eventInfo: {
endTime: '',
speakers: [{}]
},
emojiShow: false,
noticeShow: false,
sharehref: '',
isWeixinBrowser: isWinxin(),
isMobileBrowser: isMobile()
}
},
computed: {
...mapGetters(['token', 'userInfo', 'isSys', 'permissions']),
isAdmin () {
// sys、活动管理员、已报名且在直播的普通用户
return this.isSys || (this.eventInfo.ticketStatus === && this.eventInfo.liveStatus === ) || this.permissions.includes('event')
}
},
components: {
Emoji
},
methods: {
getEvent () {
getLiveEventDetailApi(this.$route.params.id).then(res => {
if (res.status === ) {
this.eventInfo = res.data
if (this.isWeixinBrowser) {
this.initShare()
}
if (this.isAdmin) {
this.fetchData()
}
}
})
},
fetchData () {
getLiveUrlApi(this.$route.params.id).then(res => {
if (res.status === ) {
this.aliPlay(res.data)
}
})
},
// 阿里云视频直播
aliPlay (source) {
let _videoDom = document.getElementById('aliVedio')
let _ch = _videoDom.clientHeight / -
let _cw = _videoDom.clientWidth / -
this.player = new Aliplayer({
id: 'aliVedio',
width: '100%',
source: source,
autoplay: true,
isLive: true,
rePlay: false,
playsinline: true,
preload: true,
controlBarVisibility: 'hover',
useH5Prism: true,
enableStashBufferForFlv: false,
stashInitialSizeForFlv: ,
skinLayout: [
{
'name': 'bigPlayButton',
'align': 'blabs',
'x': _cw,
'y': _ch
},
{
'name': 'controlBar',
'align': 'blabs',
'x': ,
'y': ,
'children': [
{
'name': 'liveDisplay',
'align': 'tlabs',
'x': ,
'y':
},
{
'name': 'fullScreenButton',
'align': 'tr',
'x': ,
'y':
},
{
'name': 'volume',
'align': 'tr',
'x': ,
'y':
}
]
}
],
components: [{
name: 'AliplayerDanmuComponent',
type: AliPlayerComponent.AliplayerDanmuComponent,
args: [this.danmukuList]
}]
}, function (player) {
player._switchLevel =
})
},
// 连接后台
connection () {
let that = this
// 建立连接对象
let sockUrl = '/api/event-websocket?token=' + this.token.substring() + '&eventId=' + this.$route.params.id
let socket = new SockJS(sockUrl)
// 获取STOMP子协议的客户端对象
this.stompClient = Stomp.over(socket)
// 定义客户端的认证信息,按需求配置
let headers = {
Authorization: ''
}
// 向服务器发起websocket连接
this.stompClient.connect(headers, (res) => {
// 订阅服务端提供的某个topic
this.stompClient.subscribe('/topic/event/' + this.$route.params.id, (frame) => {
that.addBarage(JSON.parse(frame.body))
})
that.sentFirst()
}, (err) => {
console.log('失败:' + err)
})
this.stompClient.debug = null
},
// 断开连接
disconnect () {
if (this.stompClient) {
this.stompClient.disconnect()
}
},
// 初始化websocket
initWebSocket () {
this.connection()
let that = this
// 断开重连机制,尝试发送消息,捕获异常发生时重连
this.timer = setInterval(() => {
try {
that.stompClient.send('connect-test')
} catch (err) {
console.log('断线了: ' + err)
that.connection()
}
}, )
},
// 用户加入发送弹幕
keyDown (e) {
if (e.keyCode === ) {
if (e.preventDefault) {
e.preventDefault()
} else {
window.event.returnValue = false
}
this.sentBarrage()
}
},
sentBarrage () {
this.emojiShow = false
if (this.barrageInfo.text === '' || this.barrageInfo.text === '\n') {
this.barrageInfo.text = ''
return false
}
let that = this
this.barrageInfo.eventId = this.eventInfo.id
this.barrageInfo.name = this.userInfo.account
this.barrageInfo.role = this.userInfo.roleName
if (this.permissions.length > ) {
this.barrageInfo.role = 'admin'
}
this.barrageInfo.headimgurl = this.userInfo.headimgurl
let reg = /\#[\u4E00-\u9FA5]{,}\;/gi
this.barrageInfo.comment = this.barrageInfo.text
if (reg.test(this.barrageInfo.text)) {
this.barrageInfo.text = this.barrageInfo.text.replace(reg, '')
}
this.stompClient.send(
'/msg',
{},
JSON.stringify(that.barrageInfo)
)
this.barrageInfo.text = ''
},
// 连接建立,首次发送消息
sentFirst () {
let that = this
this.barrageInfo.eventId = this.eventInfo.id
this.barrageInfo.name = this.userInfo.account
this.barrageInfo.role = this.userInfo.roleName
if (this.permissions.length > ) {
this.barrageInfo.role = 'admin'
}
this.barrageInfo.headimgurl = this.userInfo.headimgurl
this.stompClient.send(
'/msg',
{},
JSON.stringify(that.barrageInfo)
)
},
// 添加弹幕内容
addBarage (content) {
// 推送直播状态修改页面
if (content.live) {
if (this.isSys || this.permissions.includes('event')) {
return
}
this.getEvent()
}
if (content.onlineCount) {
this.total = content.onlineCount
}
let _obj = {
'mode': ,
'stime':
}
content = Object.assign(_obj, content)
this.danmukuList.push(content)
this.$nextTick(() => {
let barrage = document.getElementById('barrage')
barrage.scrollTop = barrage.scrollHeight
})
},
// 将匹配结果替换表情图片
emotion (res) {
let word = res.replace(/\#|\;/gi, '')
const list = [
'微笑', '咧嘴笑', '破涕为笑', '汗', '眯眼', '晕', '害羞', '放电',
'亲亲', '得意', '惊恐', '眼泪', '酷', '色', '吐舌', '害羞笑',
'赞', '同意', '拍手', '鼓掌', '红唇', '冷', '夜晚', '少儿不宜',
'强壮', '浪', '太阳', '香蕉', '举杯', '国旗', '心动', '奶牛',
'恶魔', '礼物', '鸡腿', '玫瑰', '西红柿', '茄子', '西瓜', '草莓'
]
let index = list.indexOf(word)
return `<img src="https://cdn.enmotech.com/attach/twemoji/${index}.png" align="middle" width="28px">`
},
setEmoji (arg) {
this.barrageInfo.text += arg
},
// 微信分享
initShare () {
let _obj = {
title: '活动直播:' + this.eventInfo.title,
img: 'https://cs.enmotech.com/image/event/event_1556264441839.png',
desc: '我正在观看' + this.eventInfo.speakers[].name + '的直播:《' + this.eventInfo.title + '》,快来围观吧!'
}
initWxShare(_obj)
}
},
mounted () {
this.sharehref = location.href
this.getEvent()
this.initWebSocket()
},
beforeDestroy () {
if (this.player) {
this.player.dispose()
this.player = null
}
// 页面离开时断开连接,清除定时器
this.disconnect()
clearInterval(this.timer)
}
}
</script>

vue-cli使用sockjs即时通信的更多相关文章

  1. Java+Netty、Vue+Element-UI实现的即时通信应用 leo-im

    之前工作接触了几个开源的IM产品,再加上曾经用Netty实现过几个服务,于是就有了用Netty实现一个IM的想法,于是用业余时间写了一个IM,和喜欢Netty的程序员们分享. 考虑到方便扩展,在服务端 ...

  2. vue cli使用融云实现聊天

    公司有个项目要实现一个聊天功能,需求如下图,略显随意 公司最终选择融云这个吊炸天的即时通信,文档详细的一匹,刚开始看文档感觉很详细实现起来也不麻烦,有很多开源的demo可以在线演示和下载 不过我们的项 ...

  3. 【原】iOS学习42即时通信之XMPP(1)

    1. 即时通信 1> 概述 即时通讯(Instant Messaging)是目前Internet上最为流行的通讯方式,各种各样的即时通讯软件也层出不穷,服务提供商也提供了越来越丰富的通讯服务功能 ...

  4. 快速上手最新的 Vue CLI 3

    翻译:疯狂的技术宅 原文:blog.logrocket.com/getting-sta- 概要:本文将指导你快速上手 Vue CLI 3,包括最新的用户图形界面和即时原型制作功能的使用步骤. 介绍 尤 ...

  5. Vue CLI 是如何实现的 -- 终端命令行工具篇

    Vue CLI 是一个基于 Vue.js 进行快速开发的完整系统,提供了终端命令行工具.零配置脚手架.插件体系.图形化管理界面等.本文暂且只分析项目初始化部分,也就是终端命令行工具的实现. 0. 用法 ...

  6. java即时通信小例子

    学习java一段时间了,今天写来一个即时通信的小例子练手在其过程中也学到了一些知识拿出来和大家分享,请路过的各位大神多多赐教... 好了下面讲一下基本的思路: 首先,编写服务器端的程序,简单点说吧就是 ...

  7. iOS开发之使用XMPPFramework实现即时通信(三)

    你看今天是(三)对吧,前面肯定有(一)和(二),在发表完iOS开发之使用XMPPFramework实现即时通信(一)和iOS开发之使用XMPPFramework实现即时通信(二)后有好多的小伙伴加我Q ...

  8. iOS开发之使用XMPPFramework实现即时通信(二)

    上篇的博客iOS开发之使用XMPPFramework实现即时通信(一)只是本篇的引子,本篇博客就给之前的微信加上即时通讯的功能,主要是对XMPPFramework的使用.本篇博客中用到了Spark做测 ...

  9. 基于XMPP协议的Android即时通信系

    以前做过一个基于XMPP协议的聊天社交软件,总结了一下.发出来. 设计基于开源的XMPP即时通信协议,采用C/S体系结构,通过GPRS无线网络用TCP协议连接到服务器,以架设开源的Openfn'e服务 ...

随机推荐

  1. idea配置web项目启动的详细说明

    每次用完一个编辑器以后 ,再换另一个编辑器使用 过段时间再回来使用idea,总是会忘记些什么  ,毕竟每个编辑器的风格和结构都有所区别 特此记下笔记   方便以后查看 图片文字看不清的   请在图片上 ...

  2. js设置元素不能编辑

    js设置元素不能编辑 $("#startLocation").attr("readOnly",true); js设置元素可以编辑 $("#startL ...

  3. JAVA中值类型和引用类型的不同(面试常考)

    转载:https://www.cnblogs.com/1ming/p/5227944.html 1. JAVA中值类型和引用类型的不同? [定义] 引用类型表示你操作的数据是同一个,也就是说当你传一个 ...

  4. python面试题之python下多线程的限制

    python多线程有个全局解释器锁(global interpreter lock). 这个锁的意思是任一时间只能有一个线程使用解释器,跟单cpu跑多个程序一个意思,大家都是轮着用的,这叫“并发”,不 ...

  5. json数组,前后端传值问题,与data时间转毫秒

    从json数组到ArrayList Gson gson = new Gson(); Car cars = gson.fromJson(result,new TypeToken<ArrayList ...

  6. 做项目单个功能的时候要理解需求和sql语句。

    做项目单个功能的时候要理解需求和sql语句.最好直接按照给出来的sql语句或者存储过程来写,避免有极其细微的差别所造成的不同. 做宜春国税二期的时候有个功能叫夜间开票情况,钻取明细时由于没理解sql语 ...

  7. UVA 315 Network (模板题)(无向图求割点)

    <题目链接> 题目大意: 给出一个无向图,求出其中的割点数量. 解题分析: 无向图求割点模板题. 一个顶点u是割点,当且仅当满足 (1) u为树根,且u有多于一个子树. (2) u不为树根 ...

  8. Python并发复习2 - 多线程模块threading

    一.多线程的调用 threading 模块建立在thread 模块之上.thread模块以低级.原始的方式来处理和控制线程,而threading 模块通过对thread进行二次封装, 提供了更方便的a ...

  9. 利用反射编写私有 Private 方法的单元测试

    利用反射编写私有 Private 方法的单元测试 最近在添加一个新feature时,鉴于要给自己的代码一是增加代码的强壮性,二是增加代码测试的覆盖率.但是遇到了有些方法是 Private 的,但是在调 ...

  10. Hessian的使用以及理解

    官网 http://hessian.caucho.com/ Hessian的使用以及理解Hessian版本:3.1.5将包括如下的内容: Hessian的基本使用Hessian的原理Hessian和S ...