websocket使用与http的区别联系

一、了解websocket

websocket是一个长链接,基于http三次握手,实时双工通讯技术(简单点说就是服务器可以给你发送消息,不需要你请求再响应)

刚开始基于http三次握手时,请求头如下:

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket // 升级成【websocket】
Connection: Upgrade // 是否升级
Sec-WebSocket-Key: x3JJHMbDL1EzLksadBhXDw== // 浏览器随机生成的base64 encode值,用于验证对方是否是websocket,实现双向通讯
Sec-WebSocket-Protocol: chat, superchat // 用户定义的字符串,一般用来传tocken
Sec-WebSocket-Version: 13 // ws的版本号
Origin: http://example.com // 请求源地址

 告诉服务器请给我升级成websocket,不要在用传统的http。

----- 到此,三次握手完成辽。。。

二、与http的区别和联系

联系

  1. 都是基于TCP协议;
  2. websocket是基于http的他们的兼容性都很好;
  3. 在连接的建立过程中对错误的处理方式相同;
  4. 都使用 Request/Response模型进行连接的建立;
  5. 都可以在网络中传输数据。

区别

  1. websocket是持久连接,http 是短连接(http可以通过Ajax一直发送请求和长轮询保持一段时间内的连接,但本质上还是短连接);
  2. websocket的协议是以 ws/wss 开头,http 对应的是 http/https;
  3. websocket是有状态的双向连接,http 是无状态的单向连接;
  4. websocket连接建立之后,数据的传输使用帧来传递,不再需要Request消息;
  5. websocket是可以跨域的。

三、WebSocket都有哪些属性、方法和事件

属性

  • Socket.readyState:这是个只读属性,y用来表示连接状态
    • 0:未连接 1:连接已建立 2.连接z正在关闭 3.连接已关闭或打不开连接
  • Socket.bufferedAmount:也是只读属性。主要是计算还没有被send()发出的UTF-8文本字节数。

方法

  • Socket.send():向服务器发送数据
  • Socket.close():关闭连接

事件

  • Socket.onopen:连接建立时触发
  • Socke.onmessage:客户端接受服务端数据时触发
  • Socket.onerror:通信错误时触发
  • Socket.onclose:连接关闭时触发

四、WebSocket 使用

类模式

import clog from '@/utils/clog' // 相当于chalk.js 控制台输出有颜色的字符串

export class Websocket {
  #pingstartTime
  #pingendTime
  #networkDelay
  constructor() {
    this.wsUrl = ''
    this.token = ''
    this.userCode = ''

    this.webscoketAgent = void (0)
    this.retryTimes = 5 // websocket重试次数
    this.retryInteval = 5 // websocket重试间隔时间 秒

    this.heartInterval = 30 // ws心跳检测时间 秒
    this.heartCount = 0
    this.heartClientTimeout = 0
    this.onMessage = (message) => {}
    this.delayCallback = (delayTimes) => {}
    this.onNetDisconnect = () => {}
  }
  // 重置重试次数
  retryTimesReset() {
    this.retryTimes = 5
  }

  /**
   * 重置心跳检测
   * @returns {SeewinWebsocket}
   */
  heartReset () {
    this.heartCount = 0
    clearInterval(this.heartClientTimeout)
    return this
  }

  /**
   * 开始心跳检测
   */
  heartPingStart () {
    const ctx = this
    ctx.heartClientTimeout = setInterval(() => {
      if (this.heartCount < 3 && typeof this.webscoketAgent !== 'undefined' && this.webscoketAgent.readyState === 1) {
        this.sendMessage('Ping', { userCode: this.userCode })
        this.pingstartTime = Date.now()
        this.heartCount++
      } else {
        this.webscoketAgent.close()
        this.heartReset()
      }
    }, this.heartInterval * 1000)
  }

  /**
   * 发送消息
   * @param sign
   * @param data
   */
  sendMessage (sign = '', data = {}) {
    if (sign === '') { return }
    // console.log('%c' + `发送时间戳${Date.now()}`, 'color:#cacaca;')
    this.sendRawMessage(JSON.stringify({ sign, data }))
  }

  /**
   * 发送ws raw消息
   * @param message
   */
  sendRawMessage (message = '') {
    if (typeof this.webscoketAgent === 'undefined') {
      clog.error('系统websocket', '发送消息失败,ws未连接')
      return
    }
    this.webscoketAgent.send(message)
  }

  /**
   * 连接ws
   * @param wsUrl ws地址
   * @param token ws连接token
   * @param userCode 用户userCode
   * @param onMessage ws消息处理回调
   * @returns {Promise<unknown>}
   */
  connect(wsUrl, token, userCode, onMessage = (message) => {}, onNetDisconnect = (message) => {}) {
    const ctx = this
    return new Promise((resolve, reject) => {
      if (wsUrl === '' || token === '' || userCode === '') {
        reject('ws地址不正确,请重新登录')
      }
      ctx.wsUrl = wsUrl
      ctx.token = token
      ctx.userCode = userCode
      this.pingstartTime = Date.now()
      ctx.webscoketAgent = new WebSocket(this.wsUrl, this.token);
      ctx.onMessage = onMessage
      ctx.onNetDisconnect = onNetDisconnect
      // 打开websocket
      ctx.webscoketAgent.addEventListener('open', (event) => {
        clog.success('系统websocket', '已连接', { uri: wsUrl, token: token })
        ctx.retryTimesReset()
        ctx.pingendTime = Date.now()
        ctx.networkDelay = ctx.pingendTime - ctx.pingstartTime
        ctx.delayCallback(ctx.networkDelay)
        // console.log(`%c[延迟]${this.networkDelay} ms`, 'color:blue;')
        ctx.heartPingStart()
      })

      // 监听websocket消息
      ctx.webscoketAgent.addEventListener('message', (event) => {
        if (typeof event.data === 'undefined' || event.data === '') { return }
        try {
          const message = JSON.parse(event.data)
          clog.info('系统websocket', '接收消息', message)
          if (message.msgType === 'PingSuccess') {
            this.pingendTime = Date.now()
            this.networkDelay = this.pingendTime - this.pingstartTime
            this.delayCallback(this.networkDelay)
            // console.log(`%c[延迟]${this.networkDelay} ms`, 'color:blue;')
          }
          ctx.onMessage(message)
        } catch (err) {
          clog.error('系统websocket', '解析消息失败', event.data)
        }
      });
      // websocket错误
      this.webscoketAgent.addEventListener('error', (event) => {
        clog.error('系统websocket', '错误', event)
        ctx.heartReset()
        setTimeout(() => {
          ctx.reload()
        }, ctx.retryInteval * 1000)
      });

      // 监听websocket消息
      this.webscoketAgent.addEventListener('close', (event) => {
        clog.warn('系统websocket', '已关闭', event)
        ctx.heartReset()
        if (ctx.retryTimes > 0) {
          setTimeout(() => {
            ctx.reload()
          }, ctx.retryInteval * 1000)
        }
      });
      resolve()
    })
  }

  /**
   * 断开websocket
   */
  disconnect() {
    if (typeof this.webscoketAgent === 'undefined') { return }
    this.wsUrl = ''
    this.token = ''
    this.userCode = ''
    this.retryTimes = 0
    console.log('退出ws', this.retryTimes)
    this.webscoketAgent.close()
  }

  /**
   * 重连
   */
  reload () {
    if (this.retryTimes <= 0) {
      clog.error('系统websocket', '重连全部完成')
      this.onNetDisconnect()
      return
    }
    clog.error('系统websocket', '正在尝试重连中...')
    this.retryTimes = this.retryTimes - 1
    this.connect(this.wsUrl, this.token, this.userCode, this.onMessage).catch(() => {})
  }
  setDelayCallback(event = (delayTimes) => {}) {
    this.delayCallback = event
  }
  /**
   * 获取延迟
   */
  getNetworkDelay() {
    return this.networkDelay
  }
}

五、携带token

三种方式

  • 通过send携带
var  ws = new WebSocket("ws://" + url + "/webSocketServer");
ws.onopen=function(){
    ws.send(token)
}
  • 在请求地址中携带
var  ws = new WebSocket("ws://" + url?token + "/webSocketServer");
var  wss = new WebSocket("wss://" + url?token + "/webSocketServer");
  • 基于协议头携带

websocket请求头中可以包含Sec-WebSocket-Protocol这个属性,该属性是一个自定义的子协议。它从客户端发送到服务器并返回从服务器到客户端确认子协议。我们可以利用这个属性添加token。

var  ws = new WebSocket("ws://" + url+ "/webSocketServer",[Protocol]); //传输组
var  ws = new WebSocket("ws://" + url+ "/webSocketServer",token); //传单个字符串

注意:如果传递了token参数,后端响应的时候,也必须携带这个token响应,否则前端收不到数据。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值