WebSocket客户端编程

通常,浏览器访问网页时,会向页面所在的服务器发送一个 HTTP 请求。Web 服务器确认请求并向浏览器发回响应。然而大多数情况下,当浏览器显示页面时实时信息已经过时了。幸运的是,随着 WebSocket 协议标准化,我们将向这一昔日窘况挥手告别。

新建连接:实例化一个 WebSocket 对象

本文主要讲述客户端编程中 WebSocket 的使用,所以不涉及服务器端配置及编码,我们就假设服务器端一切都配置好了。

为了建立一个到服务器端的连接,我们首先需要使用 WebSocket 构造函数实例化一个 WebSocket 对象:

1
const ws = new WebSocket('ws://www.websocket.org');

WebSocket 构造函数第一个参数

WebSocket 构造函数可接受两个参数,其中,第一个参数必须是以 ws://wss:// 开头的完全限定的 URL,如果 URL 有语法错误,调用构造函数时将会抛出异常。该参数是必须的,不可省略。

这两种 URL 方案实际上是由 WebSocket 协议标准规定的,两者区别如下:

  • ws:传输流量未经加密
  • wss:在 ws 基础上使用传输层安全性 TLS(SSL)加密要传输的流量

两种 URL 方案的主要区别类似 HTTP 和 HTTPS,其中 wss 安全性更高,推荐使用,不过需要服务器端和客户端双方均支持。

WebSocket 构造函数第二个参数

WebSocket 构造函数接受的第二个参数,并非必须的,其主要用于客户端和服务器端间的协议协商。

协议协商对于确定 WebSocket 服务器支持的协议及版本很有用。当应用程序支持多个协议时,我们可以通过为 WebSocket 构造函数传入第二个参数,以使用协议协商选择与特定服务器通信的协议。

该参数类型有两种可能的类型:

  • String 类型,值为客户端和服务器端均能理解的协议
  • Arrary 类型,包含一组客户端支持的协议(String 类型)
1
const ws = new WebSocket('ws://echo.websocket.org', ['myProtocol1', 'myProtocol2']);

WebSocket 事件处理

WebSocket 协议与 WebSocket API 均是事件驱动的,WebSocket 编程也遵循异步编程模式。只要 WebSocket 连接打开,应用程序就简单地监听事件。

要开始监听事件,只要为 WebSocket 事件添加回调函数即可。可以使用 addEventListener() 方法,也可以采用直接为 WebSocket 对象绑定事件处理函数的方式。

WebSocket 对象具有以下 4 个事件:

open 事件

一旦服务器响应了 WebSocket 连接请求,open 事件触并建立一个连接,open 事件对应的回调函数是 onopen()

1
2
3
4
5
6
7
8
ws.open = (event) => {
console.log('Connection open...');
}
// 或者
ws.addEventListener('open', (event) => {
console.log('Connection open...');
}, false);

open 事件触发时,服务器端与客户端之间的 WebSocket 协议握手已经完成,此时 WebSocket 已经准备好发送和接收数据。

message 事件

message 事件在接收到消息时触发,消息内容储存于事件对象 eventdata 属性中,该事件回调函数是 onmessage()

1
2
3
4
5
6
7
ws.onmessage = (event) => {
if (typeof event.data === 'string') {
console.log(`String message received: ${event.data}`);
} else {
console.log(`Other message received: ${event.data}`);
}
}

除了普通文本,WebSocket 消息内容还可以是二进制数据,这种数据作为 Blob 消息或者 ArraryBuffer 消息处理。由于设置 WebSocket 消息二进制数据类型的应用程序会影响二进制消息,所以必须在读取数据之前决定用于客户端二进制数据的类型。

1
2
3
4
5
6
7
8
ws.binaryType = 'blob';
ws.onmessage = (event) => {
if (event.data instanceof Blob) {
console.log(`Blob message received: ${event.data}`);
const blob = new Blob(event.data);
}
}

上面是 Blob 消息 message 事件处理程序示例代码,下面展示一下 ArraryBuffer 消息类型的处理过程:

1
2
3
4
5
6
7
8
ws.binaryType = 'arrarybuffer';
ws.onmessage = (event) => {
if (event.data instanceof ArraryBuffer) {
console.log(`ArraryBuffer message received: ${event.data}`);
const a = new Uint8Array(event.data);
}
}

error 事件

error 事件在响应意外故障时触发,与该事件对应的回调函数是 onerror()。错误会导致 WebSocket 连接关闭。

close 事件

close 事件在 WebSocket 连接关闭时触发,对应的回调函数是 onclose()。一旦连接关闭,客户端和服务器端之间不能再继续收发消息。

close 事件对象有 3 个有用的属性:

  • wasClean:布尔值,表示连接是否顺利关闭。如果关闭连接是对来自服务器的一个 close 帧的响应,则该属性值为 true;如果是因为其他原因关闭连接,则该属性值为 false
  • code:服务器发送的关闭连接握手状态码
  • reason:服务器发送的关闭连接握手状态

WebSocket 方法

WebSocket API 为 WebSocket 对象提供了两个方法供我们调用。

send()

使用 send() 方法可以从客户端向服务器端发送消息。当 WebSocket 在客户端与服务器端之间建立了全双工双向连接之后,才可以在连接打开时调用 send()方法。具体来说,我们应该在 open 事件触发之后,且在 close 事件触发之前调用 send() 发送消息。

1
2
3
ws.open = (event) => {
ws.send('Initial dat...');
}

除了普通文本消息,WebSocket API 还允许使用该方法发送二进制数据。

1
2
3
4
5
6
7
const binaryBlob = new Blob('blob contents');
const bianaryBuffer = new Uint8Array([8, 7, 6, 5, 4]);
ws.open = (event) => {
ws.send(binaryBlob);
ws.send(bianaryBuffer);
}

close()

通过使用 close() 方法,我们可以人为地手动关闭 WebSocket 连接或者终止连接尝试。如果连接已经关闭,那么该方法就什么都不做。

可以向 close() 方法传递两个可选参数:

  • code:Number 类型,状态代码
  • reason:String 类型,文本字符串,传递一些关于关闭连接的信息

!!!注意: 这两个可选参数与 close 事件对象的属性值一致。

1
2
3
ws.onmessage = (event) => {
ws.clsoe(1000, 'message received');
}

WebSocket 对象属性

WebSocket API 提供了多个开发者可以访问的 WebSocket 对象的属性,这些属性都从某一方面反映了 WebSocket 连接的信息。s

readyState

WebSocket 对象的 readyState 属性表示当前 WebSocket 连接状态,属性值与连接状态的对应关系如下表所示。

该属性是编程时最常使用的 WebSocket 对象属性,我们应该牢记其值所代表的 WebSocket 连接状态。

特性常量 取值 状态
WebSocket.CONNECTING 0 连接正在进行中,但还未建立
WebSocket.OPEN 1 连接已经建立
WebSocket.CLOSEING 2 连接正在进行关闭握手
WebSocket.CLOSED 3 连接已经关闭

bufferedAmount

WebSocket 对象的 bufferedAmount 属性可以用检查已经进入发送队列,但是还未发送到服务器的字节数。

值得注意的是,这个属性报告的值不包括协议组帧开销或者操作系统、网络硬件所进行的缓冲。

protocol

WebSocket 对象的 protocol 属性值为 WebSocket 打开连接握手期间,服务器端所选择的协议名。

protocol 属性在最初的握手完成之前为空,如果服务器没有选择客户端提供的某个协议,则该属性保持空值。

开发工具推荐

  • Chrome Developer Tools:监控网络流量,分析 HTTP 请求及响应头部
  • Firefox Developer Tools:监控网络流量,分析 HTTP 请求及响应头部
  • WireShark:抓取网络流量,详细分析网络协议帧、传送数据等
  • websocket-monitor:强烈推荐!!!Firefox 大法好。这是一个 Firefox Developer Tools 扩展,可以方便地监控 WebSocket 流量。也可以直接在 Firefox Add-ons 下载:https://addons.mozilla.org/en-US/firefox/addon/websocket-monitor/