Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WebSocket #54

Open
Lee981265 opened this issue Nov 3, 2020 · 0 comments
Open

WebSocket #54

Lee981265 opened this issue Nov 3, 2020 · 0 comments

Comments

@Lee981265
Copy link
Member

Lee981265 commented Nov 3, 2020

WebSokect介绍

  • WebSocket 是一种网络通信协议,很多高级功能都需要它。
  • WebSocket 协议在2008年诞生,2011年成为国际标准。所有浏览器都已经支持了
  • 它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。
  • HTTP通信只能由客户端发起,而Websocket能实现客户端服务端双向平等对话,举例来说,我们想了解今天的天气,只能是客户端向服务器发出请求,服务器返回查询结果。HTTP 协议做不到服务器主动向客户端推送信息。这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦。我们只能使用"轮询":每隔一段时候,就发出一个询问,了解服务器有没有新的信息。最典型的场景就是聊天室。
    轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)。因此,工程师们一直在思考,有没有更好的方法。WebSocket 就是这样发明的。

WebSocket特点

(1)建立在 TCP 协议之上,服务器端的实现比较容易。

(2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。

(3)数据格式比较轻量,性能开销小,通信高效。

(4)可以发送文本,也可以发送二进制数据。

(5)没有同源限制,客户端可以与任意服务器通信。

(6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。eg: ws://example.com:80/some/path

WebSocket客户端用法实例

const sokect = new WebSocket('wss://ws-test.shiguangkey.com/websocket/ocean/1604633?token=`)
  binaryType: "blob"
  bufferedAmount: 0
  extensions: ""
  onclose: null
  onerror: null
  onmessage: null
 onopen: null
 protocol: ""
 readyState: 1
 url: "ws://123.207.136.134:9010/ajaxchattest"
}`

客户端API

WebSocket构造函数

WebSocket 对象作为一个构造函数,用于新建 WebSocket 实例。
const ws = new WebSocket('ws//localhost:8080')
执行上面语句之后,客户端就会与服务器进行连接。

WebSocket常量

Constant Value Status
WebSocket.CONNECTING 0 表示正在连接
WebSocket.OPEN 1 表示连接成功,可以通信了。
WebSocket.CLOSING 2 表示连接正在关闭。
WebSocket.CLOSED 3 表示连接已经关闭,或者打开连接失败。
switch (ws.readyState) {
  case WebSocket.CONNECTING:
    // do something
    break;
  case WebSocket.OPEN:
    // do something
    break;
  case WebSocket.CLOSING:
    // do something
    break;
  case WebSocket.CLOSED:
    // do something
    break;
  default:
    // this never happens
    break;
}

WebSocket属性

Attr Note
WebSocket.binaryType 使用二进制的数据类型连接。
WebSocket.bufferedAmount 未发送至服务器的字节数(只读)。
WebSocket.extensions 服务器选择的扩展。(只读)
WebSocket.onclose 用于指定连接关闭后的回调函数。
WebSocket.onerror 用于指定连接失败后的回调函数。
WebSocket.onmessage 用于指定当从服务器接受到信息时的回调函数。。
`WebSocket.onopen 用于指定连接成功后的回调函数。
WebSocket.protocol 服务器选择的下属协议。(只读)
WebSocket.readyState 当前的链接状态。(只读)
WebSocket.url WebSocket 的绝对路径。(只读)
  • readyState 属性返回实例对象的当前状态,共有四种。 对应 WebSocket常量
  • binaryType [blob,arraybuffer] 显式指定收到的二进制数据类型。
// 收到的是 blob 数据
ws.binaryType = "blob";
ws.onmessage = function(e) {
  console.log(e.data.size);
};

// 收到的是 ArrayBuffer 数据
ws.binaryType = "arraybuffer";
ws.onmessage = function(e) {
  console.log(e.data.byteLength);
};
  • bufferedAmount 还有多少字节的二进制数据没有发送出去。它可以用来判断发送是否结束。
var data = new ArrayBuffer(10000000);
socket.send(data);

if (socket.bufferedAmount === 0) {
  // 发送完毕
} else {
  // 发送还没结束
}

WebSocket方法

FUN Note
WebSocket.close 关闭当前链接。
WebSocket.send 对要传输的数据进行排队。
  • send 向服务器发送数据。
  • 发送文本
    ws.send('your message');
  • 发送Blob对象
     var file = document  .querySelector('input[type="file"]') .files[0];
     ws.send(file);
  • 发送ArrayBuffer 对象
      var img = canvas_context.getImageData(0, 0, 400, 320);
      var binary = new Uint8Array(img.data.length);
      for (var i = 0; i < img.data.length; i++) {
          binary[i] = img.data[i];
      }
      ws.send(binary.buffer);

WebSocket事件

使用 addEventListener() 或将一个事件监听器赋值给本接口的 oneventname 属性,来监听下面的事件。

close
当一个 WebSocket 连接被关闭时触发。
也可以通过 onclose 属性来设置。
error
当一个 WebSocket 连接因错误而关闭时触发,例如无法发送数据时。
也可以通过 onerror 属性来设置.
message
当通过 WebSocket 收到数据时触发。
也可以通过 onmessage 属性来设置。
open
当一个 WebSocket 连接成功时触发。
也可以通过 onopen 属性来设置

// Create WebSocket connection.
const socket = new WebSocket('ws://localhost:8080');

// Connection opened
socket.addEventListener('open', function (event) {
    socket.send('Hello Server!');
});

// Listen for messages
socket.addEventListener('message', function (event) {
    console.log('Message from server ', event.data);
});

心跳

   private chatData: any = []
   private smartSocket= null  // socket对象
   private lockReconnect = false // 是否重连
   private tt // 重连倒计时
   private timeoutObj = null // 心跳倒计时
   private serverTimeoutObj = null // 心跳倒计时
   private timeout = 1000 * 60 * 29  // 心跳间隔29min 后台30min
    created() {
      this.initSmartSocket()
    }
    async hotQuestion() {
       const params = {
          url: 'abcd',
          data: {}
       }
       this.sendMessage(params)
   }
   handleSubmit() {
      const { textarea } = this
      if (textarea.trim() === '') {
          return this.$message.warning('请描述您的问题')
      }
      const params = {
        url: '/faq/ai/sendMsg',
        data: textarea
      }
      this.sendMessage(params)
      this.textarea = ''
  }
    initSmartSocket() {
    this.loading = true
    // 初始化所有数据
    this.chatData = []
    this.questionList = []
    // ws://123.207.136.134:9010/ajaxchattest
    this.smartSocket= new WebSocket(
      config.smartCustomerService + this.userId + `?token=${this.token}`
    )
    this.smartSocket.onopen = this.onOpen
    this.smartSocket.onmessage = this.onMessage
    this.smartSocket.onclose = this.onClose
    this.smartSocket.onerror = this.onError
  }
  // 连接成功
  onOpen() {
    console.log('WebSocket已连接', event)
    this.start()
    this.hotQuestion()
  }

  // 接收消息
  onMessage() {
    const dataBody = JSON.parse((event as any).data)
    const { index, data, msg, errorMsg } = dataBody
    console.log('mdataBodyg:::', dataBody)
    if (errorMsg) {
      this.$message.error(errorMsg)
      return (this.loading = false)
    } else {
      console.log('index:::', index)
      console.log('接收消息:::', data)
      const res = data
      const { chatData } = this
      if (res === undefined) return (this.loading = false)
      switch (index) {
        case 0: // 历史记录+欢迎语
          this.chatData = res
          this.setScorllToBottom(1000)
          break
        case 1: // 热点问题
          this.questionList = res
          break
        case 2: // 是否有帮助
          if (JSON.parse(res).isHelp === 1) {
          
          } else {
          
          }
          break
        case 3: // 答案查询
          this.chatData = chatData.concat(res)
          this.setScorllToBottom(300,{ block: 'start' })
          break
        case 4: // 心跳检测
          break
        case 5: // 发送消息
          this.chatData = chatData.concat(res)
          this.setScorllToBottom(100,{ block: 'start' })
          break

        default:
          break
      }
    }
    // this.reset()
  }
  setScorllToBottom(time,params = {}) {
    setTimeout(() => {
      let page: any = document.getElementsByClassName('text-list')
      if (page.length === 0) return (this.loading = false)
      page = page[page.length - 1]
      page.scrollIntoView(params)
      this.loading = false
    }, time)
  // 100ms js尚未执行完
  }
  // 发送消息
  sendMessage(Data) {
    console.log('发送消息:::', Data)
    this.smartSocket.send(JSON.stringify(Data))
  }

  // 连接错误
  onError() {
    console.log('连接错误')
    this.reconnect()
  }

  // 断线重连
  reconnect() {
    const { lockReconnect, tt } = this
    if (lockReconnect) return
    this.lockReconnect = true
    tt && clearTimeout(tt)
    this.tt = setTimeout(() => {
      console.log('重连中...')
      this.$message.warning('智能客服已断线线,重连中...')
      this.lockReconnect = false
      this.initSmartSocket()
    }, 2000)
  }
  reset() {
    clearTimeout(this.timeoutObj)
    clearTimeout(this.serverTimeoutObj)
    // 重启心跳
    this.start()
  }
  start() {
    // 开启心跳
    this.timeoutObj && clearTimeout(this.timeoutObj)
    this.serverTimeoutObj && clearTimeout(this.serverTimeoutObj)
    this.timeoutObj = setTimeout(() => {
      // 这里发送一个心跳,后端收到后,返回一个心跳消息,
      console.log('this.smartSocket.readyState:', this.smartSocket.readyState)
      if (this.smartSocket.readyState === 1) {
        // 如果连接正常
        const params = {
          url: '12312',
          data: {}
        }
        this.sendMessage(params)
      } else {
        // 否则重连
        this.reconnect()
      }
      this.serverTimeoutObj = setTimeout(() => {
        // 超时关闭
        this.smartSocket.close()
      }, this.timeout)
    }, this.timeout)
  }
  // 断开连接
  onClose() {
    console.log('断开连接:', event)
    clearTimeout(this.tt)
    if (!this.lockReconnect) {
      this.reconnect()
    }
  }
 beforeDestroy() {
    clearTimeout(this.timeoutObj)
    clearTimeout(this.serverTimeoutObj)
    clearTimeout(this.tt)
    this.lockReconnect = true
    this.smartSocket.close()
  }

回车键监听 按回车不换行切发送消息

<div class="input-area">
     <el-form @submit.native.prevent>
            <el-input
              type="textarea"
              placeholder="请描述您的问题"
              v-model="textarea"
              maxlength="200"
              @keydown.native="listen($event)"
            />
          </el-form>
        </div>
        <div class="footer">
          <el-button
            style="margin-right: 25px;"
            type="primary"
            size="small"
            @click="handleSubmit"
            >发送</el-button
          >
   </div>
 listen(event) {
    if (event.keyCode === 13) {
      this.handleSubmit()
      event.preventDefault()
      return false
    }
  }
@Lee981265 Lee981265 changed the title Websocket 心跳机制 Websocket Nov 7, 2020
@Lee981265 Lee981265 changed the title Websocket WebSocket Nov 7, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant