本文内容正在完善中(2022.08)…
文本介绍了 什么是 WebSockets (包括 WebSockets 的起源和 WebSockets 的概念),以及 WebSockets 的应用(包括如何搭建 WebSockets 服务端,如何在 Web 客户端中使用 WebSockets,以及如何在 小程序 中、Flutter(dart语言)中使用 WebSockets。 )
WebSockets 是一种用于在用户的浏览器和服务器之间打开 交互式通信会话的技术。使用其对应 API,可以向服务器 发送消息 并 接收事件驱动的响应,而无需通过轮询服务器的方式以获得响应。
Client-Server 模型中 Client 指的是客户端,Server 指的是服务器。服务器负责数据的管理,客户机负责完成与用户的交互任务。浏览器(Broser)是最常见的一种客户端了,在此基础上的 Broser-Server 模型上发展起来的 HTTP 协议,服务端往往永远是被动等待的,这也就意味着一般情况下只有客户端才能主动地向服务端发起通信。然而在实现某些功能的时候,我们又希望服务端同样能够向客户端主动地发送信息,比如在一个客户端请求页面后,服务端能够定时地向客户端更新推送的消息。
传统上网站为了实现推送这样的功能,采用的是轮询的方式。所谓轮询,指是在特定的的时间间隔(如每1秒)由客户端(浏览器)对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。
而比较新的技术去做轮询的效果是Comet。这种技术虽然可以双向通信,但依然需要反复发出请求。而且在Comet中,普遍采用的长链接,也会消耗服务器资源。
WebSocket 对象用于连接 WebSocket 服务器的主要接口,之后可以在这个连接上发送 和接受数据。该对象的构造函数语法格式为:
var aWebSocket = new WebSocket(url [, protocols]);
其参数描述如下:
参数 | 描述 |
---|---|
url | 要连接的 URL,这应该是 WebSocket 服务器将响应的 URL。 |
protocols | 可选,一个协议字符串或者一个包含协议字符串的数组。这些字符串用于指定子协议,这样单个服务器可以实现多个 WebSocket 子协议(例如,您可能希望一台服务器能够根据指定的协议(protocol)处理不同类型的交互)。如果不指定协议字符串,则假定为空字符串。 |
常量 | 值 |
---|---|
WebSocket.CONNECTING | 0 |
WebSocket.OPEN | 1 |
WebSocket.CLOSING | 2 |
WebSocket.CLOSED | 3 |
属性 | 描述 | 只读 |
---|---|---|
WebSocket.binaryType | 使用二进制的数据类型连接。 | |
WebSocket.bufferedAmount | 未发送至服务器的字节数。 | 是 |
WebSocket.extensions | 服务器选择的扩展。 | 是 |
WebSocket.onclose | 用于指定连接关闭后的回调函数。 | |
WebSocket.onerror | 用于指定连接失败后的回调函数。 | |
WebSocket.onmessage | 用于指定当从服务器接受到信息时的回调函数。 | |
WebSocket.onopen | 用于指定连接成功后的回调函数。 | |
WebSocket.protocol | 服务器选择的下属协议。 | 是 |
WebSocket.readyState | 当前的链接状态。 | 是 |
WebSocket.url | WebSocket 的绝对路径。 | 是 |
方法 | 描述 |
---|---|
WebSocket.close ([code[, reason]]) | 关闭当前链接。 |
WebSocket.send (data) | 对要传输的数据进行排队。 |
close
() 方法该方法用于关闭 WebSocket 连接或连接尝试(如果有的话)。 如果连接已经关闭,则此方法不执行任何操作。其语法格式为:
WebSocket.close();
send
() 方法该方法将需要通过 WebSocket 链接传输至服务器的数据排入队列,并根据所需要传输的 data bytes 的大小来增加 bufferedAmount 的值 。若数据无法传输(例如数据需要缓存而缓冲区已满)时,套接字会自行关闭。其语法格式为:
WebSocket.send(data);
其唯一参数 data 表示用于传输至服务器的数据。它必须是以下类型之一:
data 值 | 描述 |
---|---|
USVString | 表示文本字符串。 USVString 对应 unicode 标量值的所有可能序列的集合。在 JavaScript 中返回时, USVString 映射到 String 。它通常仅用于执行文本处理的 API,需要一串 unicode 标量值才能进行操作。 字符串将以 UTF-8 格式添加到缓冲区,并且 bufferedAmount 将加上该字符串以 UTF-8 格式编码时的字节数的值。 |
ArrayBuffer | ArrayBuffer 是一个字节数组,用来表示通用的、固定长度的原始二进制数据缓冲区。 你可以使用一有类型的数组对象发送底层二进制数据;其二进制数据内存将被缓存于缓冲区,bufferedAmount 将加上所需字节数的值。 |
Blob | Blob 对象表示一个不可变、原始数据的类文件对象。 它的数据可以按文本或二进制的格式进行读取。Blob 类型将队列 blob 中的原始数据以二进制中传输。 bufferedAmount 将加上原始数据的字节数的值。 |
ArrayBufferView | 您可以以二进制帧的形式发送任何 JavaScript 类数组对象 ,其二进制数据内容将被队列于缓冲区中。值 bufferedAmount 将加上必要字节数的值。 |
异常 | 描述 |
---|---|
INVALID_STATE_ERR | 当前连接未处于 OPEN 状态。 |
SYNTAX_ERR | 数据是一个包含未配对代理 (unpaired surrogates) 的字符串。 |
使用 WebSocket 对象的 addEventListener()
方法或将一个事件监听器赋值给本接口的 oneventname
属性,来监听 WebSocket 事件。WebSocket 中有以下事件:
事件 | 触发条件 | 主动触发 |
---|---|---|
close | 当一个 WebSocket 连接被关闭时触发。 | 也可以通过 onclose 属性来设置。 |
error | 当一个 WebSocket 连接因错误而关闭时触发,例如无法发送数据时。 | 也可以通过 onerror 属性来设置。 |
message | 当通过 WebSocket 收到数据时触发。 | 也可以通过 onmessage 属性来设置。 |
open | 当一个 WebSocket 连接成功时触发。 | 也可以通过 onopen 属性来设置。 |
在本小节中的案例中,socket
变量是一个通过 new WebSocket
创建的 WebSocket
连接实例:
// 创建一个 WebSocket 连接
const socket = new WebSocket('ws://localhost:8080');
关闭事件使用"close"
字符串表示,它当一个 WebSocket 连接被关闭时触发。
例如:
socket.addEventListener('close', function (event) {
console.log('WebSocket is closed now. ');
});
WebSocket.onclose
属性返回一个事件监听器,这个事件监听器将在 WebSocket 连接的readyState 变为 CLOSED时被调用,它接收一个名字为“close”的 CloseEvent 事件。其语法格式为:
WebSocket.onclose = function(event) {
console.log("WebSocket is closed now.");
};
当 websocket 的连接由于一些错误事件的发生 (例如无法发送一些数据) 而被关闭时,一个 错误事件(error event)将被引发。
例如:
// 监听可能发生的错误
socket.addEventListener('error', function (event) {
console.log('WebSocket error: ', event);
});
在 WebSocket.onerror 属性中,你可以定义一个发生错误时执行的回调函数,此事件的事件名为"error"。该属性的语法格式为:
WebSocket.onerror = function(event) {
console.error("WebSocket error observed:", event);
};
message 事件会在 WebSocket 接收到新消息时被触发。例如:
socket.addEventListener('message', function (event) {
console.log('Message from server ', event.data);
});
WebSocket.onmessage
属性是一个当收到来自服务器的消息时被调用的 event handler。它由一个MessageEvent调用。
其语法格式为:
socket.onmessage = function(event) {
console.debug("WebSocket message received:", event);
};
例如:
// 监听连接打开
socket.addEventListener('open', function (event) {
socket.send('Hello Server!');
});
WebSocket.onopen
属性定义一个事件处理程序,当WebSocket 的连接状态readyState 变为1时调用;这意味着当前连接已经准备好发送和接受数据。这个事件处理程序通过 事件(建立连接时)触发。其语法格式为:
aWebSocket.onopen = function(event) {
console.log("WebSocket is open now.");
};
连接关闭时 WebSocket 对象发送的事件。
当从服务器获取到消息的时候 WebSocket 对象触发的事件。WebSocket 接口的 onmessage
属性就是MessageEvent
对象所能代表一段被目标对象接收的消息之一。
通过事件触发的动作被定义为一个函数,该函数作为相关 message事件 ,比如使用 WebSocket 接口的 onmessage 处理器这样的事件处理器。
该对象的构造函数MessageEvent()
的语法格式为:
MessageEvent(type, init);
其中参数:
参数 | 描述 |
---|---|
type | 要创建的 MessageEvent 的类型。这可能是 XXX 中的一个 |
init | 是一个可以包含以下属性的 dictionary 对象:
|
①创建 WebSocket 对象 => ②向服务器发送数据 => ③接收服务器发送的消息 => ④关闭连接
例如:
var exampleSocket = new WebSocket("ws://www.example.com/socketserver", "protocolOne");
如果你想建立一个支持协议可选的连接,你可以指定协议的列表:
var exampleSocket = new WebSocket("ws://www.example.com/socketserver", ["protocolOne", "protocolTwo"]);
http 还是 https?
- 一旦连接建立了(也就是说 readyState 是 OPEN), exampleSocket.protocol 就会告诉你服务器选择了哪个协议。
- 上面的例子中 ws 替代了 http,同样地 wss 也会替代 https。 建立 WebSocket 链接有赖于 HTTP Upgrade mechanism, 所以当我们使用 ws://www.example.com或者 wss://www.example.com来访问 HTTP 服务器的时候协议会隐式地升级。
- 注意,与HTTP不同的是,Websocket 需要先创建连接,这就使得其成为一种有状态的协议,之后通信时可以省略部分状态信息。
微信小程序提供了以下 WebSocket 接口函数供我们使用:
接口函数 | 描述 |
---|---|
wx.sendSocketMessage(Object object) | 通过 WebSocket 连接 发送数据。 |
wx.onSocketOpen(function callback) | 监听 WebSocket 连接 打开事件。 |
wx.onSocketMessage(function callback) | 监听 WebSocket 接受到服务器的消息事件。 |
wx.onSocketError(function callback) | 监听 WebSocket 错误事件。 |
wx.onSocketClose(function callback) | 监听 WebSocket 连接关闭事件。 |
wx.connectSocket(Object object) | 创建一个 WebSocket 连接。 |
wx.closeSocket(Object object) | 关闭 WebSocket 连接。 |
SocketTask 即 WebSocket 任务,它可通过 wx.connectSocket()
接口创建返回。在微信小程序中推荐使用 SocketTask 的方式去管理 webSocket 链接,每一条链路的生命周期都更加可控。该接口提供了以下方法供我们使用:
接口函数 | 描述 |
---|---|
SocketTask.send(Object object) | 通过 WebSocket 连接发送数据 |
SocketTask.close(Object object) | 关闭 WebSocket 连接 |
SocketTask.onOpen(function callback) | 监听 WebSocket 连接打开事件 |
SocketTask.onClose(function callback) | 监听 WebSocket 连接关闭事件 |
SocketTask.onError(function callback) | 监听 WebSocket 错误事件 |
SocketTask.onMessage(function callback) | 监听 WebSocket 接受到服务器的消息事件 |
通过 tt.connectSocket 获取的 WebSocket 任务实例。
属性名 | 类型 | 说明 | 最低支持版本 |
---|---|---|---|
readyState | number | undefined | 表示 Socket 连接状态 code; 若由于参数错误导致未创建连接, 则 | 为 undefined | 1.0.0 |
CONNECTING | 0 | 表示 Socket 正在连接 | 1.0.0 |
OPEN | 1 | 表示 Socket 连接已经打开 | 1.0.0 |
CLOSING | 2 | 表示 Socket 连接关闭中 | 1.0.0 |
CLOSED | 3 | 表示 Socket 连接已关闭 | 1.0.0 |
方法 | 描述 | 最低版本 |
---|---|---|
send | 通过 WebSocket 连接发送数据 | 1.0.0 |
close | 关闭 WebSocket 连接 | 1.0.0 |
onOpen | 监听 WebSocket 连接服务器成功的事件 | 1.0.0 |
onClose | 监听 WebSocket 与服务器的连接断开的事件 | 1.0.0 |
onMessage | 监听 WebSocket 接收到服务器发送信息的事件 | 1.0.0 |
onError | 监听 WebSocket 发生错误的事件 | 1.0.0 |
方法 | 描述 |
---|---|
my.connectSocket | 用于创建一个 WebSocket 的连接。 |
my.closeSocket | 关闭 WebSocket 连接的 API。 |
my.offSocketClose | 取消监听 WebSocket 关闭事件的 API |
my.offSocketOpen | 取消监听 WebSocket 连接打开事件的 API。 |
my.offSocketError | 取消监听 WebSocket 错误事件的 API。 |
my.onSocketClose | 监听 WebSocket 关闭事件的 API。 |
my.onSocketError | 监听 WebSocket 错误事件的 API。 |
my.onSocketMessage | 监听 WebSocket 接受到服务器的消息事件的 API。 |
my.onSocketOpen | 监听 WebSocket 连接打开事件的 API。 |
my.sendSocketMessage | 通过 WebSocket 连接发送数据,需要先使用 my.connectSocket 建立连接,在调用 my.onSocketOpen |
在dart语言中使用 WebSocket 和在 JavaScript使用 WebSocket 是很类似的。 要在 Web 应用程序中使用 WebSocket,首先创建一个 WebSocket 对象,将 WebSocket URL 作为参数传递给构造函数。例如:
var socket = new WebSocket('ws://127.0.0.1:1337/ws');
然后,你可以使用 send 方法在 WebSocket 实例上发送数据,例如:
if (socket != null && socket.readyState == WebSocket.OPEN) {
socket.send(data);
} else {
print('WebSocket not connected, message $data not sent');
}
在javascript中的WebSocket实例上,我们使用onmessage
属性,表示一个当收到来自服务器的消息时被调用的 event handler,就像这样:
// javascript 中,详见本文 2.1.3.3节:消息事件
socket.onmessage = function(event) {
console.debug("WebSocket message received:", event);
};
在 dart 语言中也是类似,要在 WebSocket 上接收数据,请为消息事件注册一个侦听器。:
// dart 语言中
socket.onMessage.listen((MessageEvent e) {
receivedData(e.data);
});
WebSocket 类构造器的语法格式为:
WebSocket(
String url,
[Object? protocols]
)
与 JavaScript 中的WebSocket类一样,dart 的WebSocket 类也提供了一些常量:
常量 | 值 |
---|---|
CONNECTING | 0 |
OPEN | 1 |
CLOSING | 2 |
CLOSED | 3 |
属性 | 类型 | 读写 | 描述 | inherited |
---|---|---|---|---|
binaryType | String? | 读 / 写 | 使用二进制的数据类型连接。 | |
bufferedAmount | int? | 只读 | 未发送至服务器的字节数。 | |
extensions | String? | 只读 | 服务器选择的扩展。 | |
onClose | Stream | 只读 | 由此WebSocket处理的关闭事件流。 | |
onError | Stream | 只读 | 此WebSocket处理的错误事件流。 | |
onMessage | Stream | 只读 | 由此WebSocket处理的消息事件流。 | |
onOpen | Stream | 只读 | 由此WebSocket处理的打开事件流。 | |
protocol | String? | 只读 | 一个协议字符串,这些字符串用于指定子协议,这样单个服务器可以实现多个 WebSocket 子协议(例如,您可能希望一台服务器能够根据指定的协议(protocol)处理不同类型的交互)。如果不指定协议字符串,则假定为空字符串。 | |
readyState | int | 只读 | 当前的链接状态 | |
url | String? | 只读 | WebSocket 的绝对路径 | |
runtimeType | Type | 只读 | 对象运行时类型的表示形式。 | 是 |
hashCode | int | 只读 | 此对象的哈希代码。[…] | 是 |
on | Events | 只读 | 对于事件流来说,这是一个易于使用的访问器,只应在显式访问器不可用时使用。 | 是 |
方法名 | 返回类型 | 描述 | 是否是继承的 |
---|---|---|---|
addEventListener (String type, EventListener? listener, [bool? useCapture]) | void | 添加事件监听器 | 是,从EventTarget 类继承 |
removeEventListener (String type, EventListener? listener, [bool? useCapture]) | void | 移除事件监听器 | 是,从EventTarget 类继承 |
dispatchEvent (Event event) | bool | 是,从EventTarget 类继承 | |
noSuchMethod (Invocation invocation) | dynamic | 当访问不存在的方法或属性时调用。 | 是,从Object 继承 |
toString () | String | 此对象的字符串表示形式。 | 是,从Object 继承 |
close ([int? code, String? reason]) | void | 关闭当前链接。 | |
send (dynamic data) | void | 通过此连接向服务器传输数据。 | |
sendBlob (Blob data) | void | 通过此连接向服务器传输数据。 | |
sendByteBuffer (ByteBuffer data) | void | 通过此连接向服务器传输数据。 | |
sendString (String data) | void | 通过此连接向服务器传输数据。 | |
sendTypedData (TypedData data) | void | 通过此连接向服务器传输数据。 |
EventTarget
类它支持事件的所有浏览器对象的基类,使用它的 on
属性添加和移除编译时类型检查和更简洁的API的事件。有些类似于 JavaScript 中的MessageEvent 对象(参考本文 2.2.2 节)。
该类有以下属性:
属性 | 类型 | 描述 | 只读 | inherited |
---|---|---|---|---|
hashCode | int | 此对象的哈希代码。 | 是 | 是 |
runtimeType | Type | 对象运行时类型的表示形式。 | 是 | 是 |
on | Events | 对于事件流来说,这是一个易于使用的访问器,只应在显式访问器不可用时使用。 | 是 |
[1] mozilla.MDN 文档
[2] 陈云贵、高旭.微信小程序开发从入门到实战.清华大学出版社
[3] Tencent.微信小程序文档
[4] Alipay.支付宝小程序文档
[5] bytedance.字节小程序文档