vue-cli使用sockjs即时通信


  基于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: 0, 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 === 2 && this.eventInfo.liveStatus === 1) || this.permissions.includes('event') } }, components: { Emoji }, methods: { getEvent () { getLiveEventDetailApi(this.$route.params.id).then(res => { if (res.status === 200) { 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 === 200) { this.aliPlay(res.data) } }) }, // 阿里云视频直播
 aliPlay (source) { let _videoDom = document.getElementById('aliVedio') let _ch = _videoDom.clientHeight / 2 - 32 let _cw = _videoDom.clientWidth / 2 - 32
        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: 32, skinLayout: [ { 'name': 'bigPlayButton', 'align': 'blabs', 'x': _cw, 'y': _ch }, { 'name': 'controlBar', 'align': 'blabs', 'x': 0, 'y': 0, 'children': [ { 'name': 'liveDisplay', 'align': 'tlabs', 'x': 15, 'y': 6 }, { 'name': 'fullScreenButton', 'align': 'tr', 'x': 10, 'y': 14 }, { 'name': 'volume', 'align': 'tr', 'x': 5, 'y': 13 } ] } ], components: [{ name: 'AliplayerDanmuComponent', type: AliPlayerComponent.AliplayerDanmuComponent, args: [this.danmukuList] }] }, function (player) { player._switchLevel = 0 }) }, // 连接后台
 connection () { let that = this
        // 建立连接对象
        let sockUrl = '/api/event-websocket?token=' + this.token.substring(7) + '&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() } }, 5000) }, // 用户加入发送弹幕
 keyDown (e) { if (e.keyCode === 13) { 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 > 0) { this.barrageInfo.role = 'admin' } this.barrageInfo.headimgurl = this.userInfo.headimgurl let reg = /\#[\u4E00-\u9FA5]{1,4}\;/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 > 0) { 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': 1, 'stime': 1000 } 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[0].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>

 

 

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM