聊天功能开发中所遇到的问题

一直以来我认为Websocket就是长连接。长连接就是客户端和服务器一直保持连接,并且两者都可以主动发消息给另一方

基于现在的理解列出上面这句话中错误的知识点:

  • websocket和长连接是两个不同的概念
  • 主动发消息的功能不是长连接的特点
  • 加粗部分对长连接这个概念的描述不够透彻,太过片面,不算错误(后面会讲到)

概念混淆(长连接,Websocket)

长连接:一个网站中通常会有很多请求,如果每个请求都需要进行TCP连接的建立和断开,那么是相当耗时的,处理速度也会大大降低,在Http 1.1中支持进行长连接(Connection: keep-alive),即:一个http操作建立TCP连接后,进行数据传输,结束后并不会立即断开而是保持连接,后面的http请求使用同一个连接进行数据传输,不需要再次建立连接,始终使用一个TCP连接

Websocket:一种在单个TCP连接上进行全双工通信的协议。使得客户端和服务器之间的数据交换更加简单,允许服务端主动向客户端推送数据。在Websocket的API中,浏览器和服务器只要完成一次握手,就可以创建持久性的连接,并进行双向数据传输。(HTML5中新定义的协议,可以很好的节省服务器的资源和带宽,实时进行通讯)

根据对这两个概念的理解,得出Websocket协议本身就实现了长连接,因为它是基于单个TCP连接进行通信,在此基础上还实现了服务器可主动发送数据,进行双向通信的功能

为什么要用Websocket

有这样的需求场景:很多网站需要实现推送功能,这就需要服务器主动向浏览器发送请求,但是Http协议的弊端就是通信只能由客户端发起,那么我们为了实现推送功能,就需要客户端不停的去问服务器,于是就有了下面的对话。这就是最常见的轮询技术,要定时的向服务器发请求,但是请求中可能包含较长的头部,真正有效的数据只有小部分,因此这种方式会浪费很多资源,增加服务器压力。而Websocket实现了服务器主动向客户端推送消息,因此像聊天室这种业务场景更多的会使用Wescoket来实现

client:你有没有消息哇。。。
server:暂时没有,你过会再来
client:我又来了,现在有没有。。。
server:有了有了,给你拿去。。。

Websocket使用的关键点——心跳保活策略

Websocket提供的API使用都比较简单,这里不再赘述,主要想说心跳保活的问题,因为在连接建立后,有可能存在网络断开或者别的异常情况,这个时候是没法触发浏览器或者服务器的onclose事件,无法知道断开连接,也就无法进行重连,并且收发的数据也会丢失掉,因此我们就有了Websocekt的心跳,就是说明连接还处于正常状态。

心跳的检测的机制:每隔一段时间客户端向服务器发送一个心跳包,服务器收到后会返回一个数据包,以此来确认两者都活着,否则任意一方在规定时间内没有收到心跳包,则会认定网络断开,需要重连。若重连N次还是失败,则会提示用户网络问题,依赖着心跳检测才可以保障长连接的存在

前后端约定的规则:

  • 每隔30秒发送一次心跳包
  • 6秒内未返回心跳包断开重连
  • 重连超过10次不再连接
createWebSocket () {
      this.ws = new WebSocket(`ws://localhost:3000`)

      this.ws.onopen = () => {
        this.reconnectCount = 0
        this.heartCheck().start()
        console.log('Connection to server opened')
      }

      this.ws.onmessage = (event) => {
        const response = JSON.parse(event.data)
        this.heartCheck().reset()
        console.log('Client received a message', response)
      }

      this.ws.onclose = () => {
        console.log('connection closed')
        clearTimeout(this.heartTimer)
        this.reconnect()
      }
    }

    // ws重连操作
    reconnect () {
      this.reconnectCount = this.reconnectCount + 1
      console.log('重连' + this.reconnectCount + '次')
      if (this.reconnectCount > 10) {
        throw new Error('重新连接失败')
      }
      this.createWebSocket()
    }

    // 心跳检测
    heartCheck () {
      const _self = this
      return {
        start: function () {
          _self.heartTimer = setTimeout(function () {
            _self.ws.send(JSON.stringify({
              'type': 1,
              'timestamp': new Date().getTime()
            }))
            _self.closeTimer = setTimeout(() => _self.ws.close(), 6000) // 超过6秒未返回心跳包断开连接
          }, 30000) // 每隔30秒发心跳包检测
        },
        reset: function () {
          clearTimeout(_self.heartTimer)
          clearTimeout(_self.closeTimer)
          this.start()
        }

      }
    }

页面滑动的分页功能

需求:在联系人列表页滑动,当滚动条距离底部小于50px触发请求获取第二页的数据然后展示

问题拆解:

  • 监听dom的滚动事件
  • 计算滚动条距离底部的距离

对Dom进行事件监听比较容易,我当时在第二个问题上花的时间比较久,最终明白了如何计算。
先搞明白Dom的三个属性:scrollHeight、clientHeight、scrollTop

  • scrollHeight:容器里所有内容的高度
  • clientHeight:容器的高度,可见高度
  • scrollTop:滚动条滚动过的距离

举例:一个DIV的高度是400px(clientHeight),里面有一个很长的列表,这个列表的高度是1000px(scrollHeight),说明还有600px的内容是不可见的,要想显示剩余的内容,就需要拖动滚动条来显示。当不拉滚动条时,此时scrollTop是0,如果把滚动条拉到底,剩下600px的内容显示出来,此时scrollTop就是600,于是我们认为scrollTop就是滚动条可以滚动的距离,也就是剩余内容的高度[0, 600]
scrollHeight - clientHeight - scrollTop = 滚动条距离底部的距离

有了这个公式我们的滑动分页功能就没多大问题了
但是在后面自测的时候发现:在滑动的过程中不可能精准的在小于50px的范围内停一次,有可能在45,34用户都停下了,这就导致请求发了不止一次,而我们理想是只发一次。最后我用一个变量isLoading去判断,请求发出后isLoading为true,回来后改为false,在scroll事件处理函数中去判断这个变量的值,为true,则不发请求。

 // 滚动条距离底部小于50px时触发请求
    handleScrollEvent () {
      const element = this.$refs.scrollUserList
      if (element.scrollHeight - element.clientHeight - element.scrollTop < 50 && !this.isLoading) {
        this.page += 1
        this.getFriendsList(this.page)
      }
    }

    async getFriendsList (page = 0) {
      this.isLoading = true
      const params = {
        wechatId: localStorage.getItem('loginWechatId'),
        productId: 211,
        token: 'dalfdll',
        page,
        limit: 20
      }

      const response = await chatService.getFriendsList(params)
      const { success } = response

      if (success) {
        this.isLoading = false
      }
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容