浅谈webscoket原理及其应用

2,369 阅读3分钟

什么是 webSocket

WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议。使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。

在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

WebSocket 解决了什么问题:

在不使用 WebSocket 时,如果我们需要建立一条长连接,有以下几种方法:

  • 轮询
  • 长轮询(常用)
  • SSE(Server Send Event)

当出现类似体育赛事、聊天室、实时位置之类的场景时,客户端要获取服务器端的变化,就只能通过轮询(定时请求)来了解服务器端有没有新的信息变化。WebSocket 的出现,让服务器端可以主动向服务器端发送信息,使得浏览器具备了实时双向通信的能力,这就是 WebSocket 解决的问题

  • 带宽问题:WebSocket 相对于 HTTP 来说协议头更加小,同时按需传递。
  • 数据实时性问题:WebSocket 相对于轮询和长轮询来说,能够实时传递数据,延迟更小。
  • 状态问题:相较于 HTTP 的无状态请求,WebSocket 在建立连接后能够维持特定的状态。

WebSocket 与 HTTP 对比

基本使用

客户端

const ws = new WebSocket('ws://localhost:8888')

ws.onopen = () => {
  console.log('WebSocket onopen')
}

ws.onmessage = e => {
  console.log(e)
  console.log(e.data)
}

ws.onclose = e => {
  console.log('WebSocket onclose')
}

ws.onerror = e => {
  console.log('WebSocket onerror')
}
  • WebSocket.onopen: 连接成功后调用
  • WebSocket.onmessage: 当接收到服务器消息时调用
  • WebSocket.onclose: 连接关闭后调用
  • WebSocket.onerror: 发生错误后调用

服务端例子(koa)

const Koa = require('koa')
const WebSocket = require('ws')

const app = new Koa()
const ws = new WebSocket.Server({ port: 8888 })

ws.on('connection', ws => {
  console.log('server connection')

  ws.on('message', msg => {
    console.log('server receive msg:', msg)
  })

  ws.send('Information from the server')
})

app.listen(3000)

WebSocket 可以传递 String、ArrayBuffer 和 Blob 三种数据类型,因此在收到消息时可能是其中的任意一种。其中,String 和 ArrayBuffer 使用的最多。

  • 如果是 String 类型,直接通过字符串处理函数即可进行相关转换,如 JSON 等格式。
  • 如果是二进制 blob 类型,则需要使用 ArrayBuffer 和 DataView 来进行处理,下面简单介绍。

二进制数据包括:blob 对象和 Arraybuffer 对象,所以我们需要分开来处理。

// 接收数据
ws.onmessage = function(event) {
  if (event.data instanceof ArrayBuffer) {
    // 判断 ArrayBuffer 对象
  }
  if (event.data instanceof Blob) {
    // 判断 Blob 对象
  }
}

// 发送 Blob 对象的例子
let 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.bufferedAmount 属性,表示还有多少字节的二进制数据没有发送出去 如果发送的二进制数据很大的话,可以这样判断

var data = new ArrayBuffer(10000000)
socket.send(data)
if (socket.bufferedAmount === 0) {
  // 发送完毕
} else {
  // 发送还没结束
}

总结 WebSocket 的优点

  • 双向通信(一开始说的,也是最重要的一点)。
  • 数据格式比较轻量,性能开销小,通信高效
  • 协议控制的数据包头部较小,而 HTTP 协议每次通信都需要携带完整的头部
  • 更好的二进制支持
  • 没有同源限制,客户端可以与任意服务器通信
  • 与 HTTP 协议有着良好的兼容性。默认端口也是 80 和 443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器