一、产生背景
在Web开发中,我们经常和HTTP协议打交道,它是一个请求-响应
的通信方式。但是除了HTTP 2.0版本以外,服务器端是无法主动发送消息的,它只支持单向数据通信的方式。
如果要去实现一个以双向通信方式的简易聊天室,浏览器需要以一种轮询
的方式去不停地发送AJAX请求更新数据,这样做会造成资源高消耗和低性能。于是WebSocket协议就这么诞生了,虽然它诞生的时间比HTTP协议晚,但它实现的功能比HTTP更加成熟和可靠。
WebSocket最初在HTML5规范中被引用为TCPConnection
,作为基于TCP的套接字API的占位符。2008年6月,Michael Carter进行了一系列讨论,最终形成了称为WebSocket的协议。
二、特点
WebSocket是一种网络传输协议,可在单个TCP连接上进行全双工通信,位于OSI模型的应用层。
补充一个小知识:TCP上的连接通信分为半双工通信
、全双工通信
、单向通信
三种方式。
目前大部分浏览器均已支持WebSocket,可以放心使用。
它主要特点如下:
- 较少的控制开销。在连接创建后,服务器和客户端之间交换数据时,用于协议控制的数据包头部相对较小,而HTTP协议每次请求需要携带完整的头部。
- 更强的实时性。支持全双工通信,低消耗,高性能。
- 可以保持连接状态。与HTTP不同的是,Websocket需要先创建连接,这就使得其成为一种有状态的协议,之后通信时可以省略部分状态信息。而HTTP请求可能需要在每个请求都携带状态信息(如身份认证等)。
- 更好的二进制支持。Websocket定义了二进制帧,相对HTTP,可以更轻松地处理二进制内容。
- 支持跨域。(这个太强了!)
- 与 HTTP 协议有着良好的兼容性。默认端口也是80(ws)和443(wss),并且握手阶段采用 HTTP 协议中的
Upgrade
头来升级协议,支持HTTP代理和中介。 - 可以支持扩展。Websocket定义了扩展,用户可以扩展协议、实现部分自定义的子协议。如部分浏览器支持压缩等。
- 更好的压缩效果。相对于HTTP压缩,Websocket在适当的扩展支持下,可以沿用之前内容的上下文,在传递类似的数据时,可以显著地提高压缩率。
思考:从上面的特点可以看出,同样是应用层上的协议,WebSocket协议要比HTTP协议要优秀很多。所以,数据传输都用WebSocket协议它不香吗?(没得标准答案,我自己也要思考。我没有歧视HTTP协议的意思啊…😝)
三、客户端API介绍
WebSocket提供了用于创建和管理 WebSocket 连接以及可以通过该连接发送和接收数据的 API。
我们可以通过创建一个实例对象ws
建立与本地服务器(localhost:8080)的WebSocket连接:
let ws = new WebSocket('ws://localhost:8080')
下面的案例和知识都会围绕这个实例对象来展开讲述。
3.1 常量
下表是WebSocket
构造函数的原型中存在的一些常量介绍:
Constant | Value |
---|---|
WebSocket.CONNECTING(正在连接中) | 0 |
WebSocket.OPEN(连接已打开,可以通信) | 1 |
WebSocket.CLOSING (连接正在关闭) | 2 |
WebSocket.CLOSED (表示连接已经关闭,或者打开连接失败。) | 3 |
WebSocket
和XMLHttpRequest
对象很相似,它们都可以通过readyState
属性来判断当前连接状态(XMLHttpRequest
对象有5个状态)。
if (ws.readyState === WebSocket.OPEN) {
// 连接建立后做点啥...
}
3.2 常见属性
WebSocket.bufferedAmount
:是一个只读属性,用于返回已经被send
方法放入队列中但还没有被发送到网络中的数据的字节数。一旦队列中的所有数据被发送至网络,则该属性值将被重置为0。但是,若在发送过程中连接被关闭,则属性值不会重置为0。它可以用来判断二进制数据发送是否结束。WebSocket.onclose
:用于指定连接关闭后的回调函数。WebSocket.onerror
:用于指定连接失败后的回调函数。WebSocket.onmessage
:用于指定当从服务器接受到信息时的回调函数。WebSocket.onopen
:用于指定连接成功后的回调函数。WebSocket.readyState
: 只读属性,表示当前的连接状态。
3.3 方法
WebSocket.send
:发送数据。数据类型只能是utf-8
编码的字符串和二进制数据(Blob
或ArrayBuffer
)这两种。WebSocket.close
:关闭连接。
这里补充一个小知识:Blob
对象和ArrayBuffer
对象的区别在于前者用于操作二进制文件,而后者用于操作内存(模拟内存)。如果大家对这个小知识感兴趣可以去看这篇文章。
结合上述提及的WebSocket
的属性和方法,我给出一个浏览器端简单使用WebSocket的例子:
demo.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
</head>
<body>
<input type="file" name="filename" />
<button>关闭连接</button>
<script>
let ws = new WebSocket("ws://localhost:8080");
let buffer = new ArrayBuffer(8