WebSocket

更新时间: 2024-04-11 15:18:39

Web Socket(套接字)的目标是通过一个长时连接实现与服务器全双工、双向的通信。在JavaScript中创建Web Socket时,一个HTTP请求会发送到服务器以初始化连接。服务器响应后,连接使用HTTP的Upgrade头部从HTTP协议切换到Web Socket协议。这意味着Web Socket不能通过标准HTTP服务器实现,而必须使用支持该协议的专有服务器。

因为Web Socket使用了自定义协议,所以URL方案(scheme)稍有变化:不能再使用http://或https://,而要使用ws://和wss://。前者是不安全的连接,后者是安全的连接。在指定Web Socket URL时,必须包含URL方案,因为将来可能再支持其他方案。

使用自定义协议而非HTTP协议的好处是,客户端与服务器之间可以发送非常少的数据,不会对HTTP造成任何负担。使用更小的数据包让Web Socket非常适合带宽和延迟问题比较明显的移动应用。使用自定义协议的缺点是,定义协议的时间比定义Javascript API要长。Web Socket得到了所有主流浏览器支持。

# API

要创建一个新的Web Socket,就要实例化一个WebSocket对象并传入提供连接的URL:

let socket = new WebSocket("ws://www.example.com/server.php");
1

注意

必须给WebSocket构造函数传入一个绝对URL。同源策略不适用于WebSocket,因此可以打开任意站点的连接。至于是否来自特定源的页面通信,则完全取决于服务器(在握手阶段就可以确定请求来自哪里。)

浏览器会在初始化WebSocket对象之后立即创建连接。与XHR类似,WebSocket也有一个readyState属性表示当前状态。不过这个值与XHR中相应的值不一样。

实际值 含义
WebSocket.OPENING 0 连接正在建立
WebSocket.OPEN 1 连接已经建立
WebSocket.CLOSING 2 连接正在关闭
WebSocket.CLOSE 3 连接已经关闭

WebSocket对象没有readystetechange事件,而是有与上述不同状态对应的其他事件。readyState值从0开始。

任何时候读可以调用close()方法关闭Web Socket连接:

socket.close()
1

调用close()之后,readyState立即变为2(连接正在关闭),并会在关闭后变为3(连接已经关闭)。

# 发送和接收数据

打开Web Socket之后,可以通过连接发送和接收数据。要向服务器发送数据,使用send()方法并传入一个字符串、ArrayBuffer或Blob:

let socket = new WebSocket("wa://www.example.com/server.php")

let stringData = "Hello world!"
let arrayBufferData = Uint8Array.from(['f','o','o'])
let blobData = new Blob(['f','o','o'])

socket.send(stringData)
socket.send(arrayBufferData.buffer)
socket.send(blobData)
1
2
3
4
5
6
7
8
9

服务器向客户发送消息时,WebSocket对象上会触发message事件。这个message事件与其他消息协议类似,可通过event.data属性访问到有效载荷:

socket.onmessage = function(evnet) {
    let data = event.data
}
1
2
3

与通过send()方法发送的数据类似,event.data返回的数据也可能是ArrayBuffer或Blob。这由WebSocket对象的binaryType属性决定,该属性可能是blob或arrayBuffer。

# 其他事件

WebSocket对象在连接生命周期中可能触发3个其他事件。

事件名 事件
open 在连接成功时触发
error 在发生错误时触发,连接无法存续
close 在连接关闭时触发

注意

WebSocket对象不支持DOM Level2事件监听器,因此需要使用DOM Level 0 风格的事件处理程序来监听这些事件:

let socket = new WebSocket("wa://www.example.com/server.php")
socket.onopen = funciton() {
  alert("Connection established")
}
socket.onerror = funciton() {
  alert("Connection error")
}
socket.onclose = function() {
  alert("Connection closed")
}
1
2
3
4
5
6
7
8
9
10

在这些事件中,只有close事件的event对象上有额外信息。
这个对象上有3个额外属性:

  • wasClean : 一个布尔值,表示连接是否干净地关闭
  • code : 一个来自服务器的数值状态码
  • reason :一个字符串,包含服务器发来的消息

可以将这些信息显示给用户或记录到日志:

socket.onclose = function(event) {
  console.log(event.wasClean, event.code, event.reason)
}
1
2
3