携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情
【HTML】深入WebSocket的世界
引言
github:【HTML】深入WebSocket的世界
内容速递:看了本文您能了解到的知识
1、什么是WebSocket
WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。
2、WebSocket与HTTP
说到WebSocket那就离不开它的好兄弟HTTP,它们都位于OSI模型的应用层,并且都依赖于传输层的TCP协议。
但是,说到这里并不足以说明它们之间的关系,不是吗?在RFC 6455中找到:
it is designed to work over HTTP ports 80 and 443 as well as to support HTTP proxies and intermediaries.
WebSocket通过HTTP端口80和443进行工作,并支持HTTP代理和中介。
看一张图,对比一下两者的差异
WebSocket通过HTTP1.1 协议的101状态码进行握手。为了创建Websocket连接,需要通过浏览器发出请求,之后服务器进行回应,这个过程通常称为握手(handshaking) 。
3、WebSocket的好处
说到WebSocket的好处,那就得看看为什么需要WebSocket?大家都知道HTTP协议,但是用的多的人,大都知道HTTP有一个明显的缺陷,那就是请求只能是由客户端发起。
3.1、轮询
在遇到实时性要求比较高的需求的时候,需要频繁的使用HTTP向服务端拉取数据。这种方式就叫做轮询:最典型的场景就是聊天室。
轮询的方式有几个比较明显的特点:
1、需要不断发送HTTP请求,效率低,非常浪费资源。
2、不能保证及时性。在请求的间隙中,无法及时获取服务端最新的数据。
因此,工程师们一直在思考,有没有更好的方法,将偷懒(效率)执行下去。于是WebSocket应运而生!
3.2、轮询的进化
WebSocket虽然是一个新的技术,但是它基于TCP协议,对服务端天然友好,它的端口号也是80和443,并且握手阶段采用 HTTP 协议。这样看来使用WebSocket的代价是比较能接受的。但要说到是轮询的进化,那么它的优势必然不止于此!
WebSocket优势:
1、建立在 TCP 协议之上,服务器端的实现比较容易。
2、与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。
eg:ws://127.0.0.1 wss://127.0.0.1
3、数据格式比较轻量,性能开销小,通信高效。
4、可以发送文本,也可以发送二进制数据。
5、没有同源限制,客户端可以与任意服务器通信。
4、WebSocket的客户端API
4.1、WebSocket 构造函数
WebSocket()构造函器会返回一个 WebSocket对象。
语法:
const ws = new WebSocket(url [, protocols]);
参数:
-
url要连接的 URL;这应该是 WebSocket 服务器将响应的 URL。
-
protocols可选一个协议字符串或者一个包含协议字符串的数组。这些字符串用于指定子协议,这样单个服务器可以实现多个 WebSocket 子协议(例如,您可能希望一台服务器能够根据指定的协议(
protocol)处理不同类型的交互)。如果不指定协议字符串,则假定为空字符串。
示例:
// 客户端与服务器进行连接
const ws = new WebSocket('ws://localhost:8080');
4.2、WebSocket.readyState
readyState属性返回实例对象的当前状态
语法:
const readyState = WebSocket.readyState;
状态:
| 状态 | 说明 |
|---|---|
| CONNECTING | 值为0,表示正在连接。 |
| OPEN | 值为1,表示连接成功,可以通信了。 |
| CLOSING | 值为2,表示连接正在关闭。 |
| CLOSED | 值为3,表示连接已经关闭,或者打开连接失败。 |
示例:
switch (ws.readyState) {
case WebSocket.CONNECTING:
// do something
break;
case WebSocket.OPEN:
// do something
break;
case WebSocket.CLOSING:
// do something
break;
case WebSocket.CLOSED:
// do something
break;
default:
// this never happens
break;
}
4.3、WebSocket.onopen
**WebSocket.onopen**属性定义一个事件处理程序,当WebSocket的连接状态readyState变为1时调用;
这意味着当前连接已经准备好发送和接受数据。这个事件处理程序通过 事件(建立连接时)触发。
语法:
ws.onopen = function(event) {
console.log('WebSocket is open now.');
};
如果要指定多个回调函数,可以使用addEventListener方法。
ws.addEventListener('open', function (event) {
ws.send('WebSocket is open now.');
});
4.4、WebSocket.onclose
WebSocket.close() 方法关闭 WebSocket连接或连接尝试(如果有的话)。 如果连接已经关闭,则此方法不执行任何操作。
语法:
ws.onclose = function(event) {
console.log('WebSocket is close now.');
}
如果要指定多个回调函数,可以使用addEventListener方法。
ws.addEventListener('close', function (event) {
ws.send('WebSocket is close now.');
});
参数:
回调的参数:CloseEvent
4.5、WebSocket.send
WebSocket.send() 方法将需要通过 WebSocket 链接传输至服务器的数据排入队列,并根据所需要传输的 data bytes 的大小来增加 bufferedAmount的值 。若数据无法传输(例如数据需要缓存而缓冲区已满)时,套接字会自行关闭。
语法:
WebSocket.send("Hello server!");
参数:
data:用于传输至服务器的数据。它必须是以下类型之一:
USVString:文本字符串。字符串将以 UTF-8 格式添加到缓冲区,并且bufferedAmount将加上该字符串以 UTF-8 格式编码时的字节数的值。
ArrayBuffer:您可以使用一有类型的数组对象发送底层二进制数据;其二进制数据内存将被缓存于缓冲区,bufferedAmount将加上所需字节数的值。
Blob:Blob类型将队列 blob 中的原始数据以二进制中传输。bufferedAmount将加上原始数据的字节数的值。
ArrayBufferView:您可以以二进制帧的形式发送任何 JavaScript 类数组对象;其二进制数据内容将被队列于缓冲区中。值bufferedAmount将加上必要字节数的值。
4.6、WebSocket.binaryType
WebSocket.binaryType 返回 websocket 连接所传输二进制数据的类型。
语法:
const binaryType = ws.binaryType;
返回值:
blob:如果传输的是Blob类型的数据。
arraybuffer:如果传输的是ArrayBuffer类型的数据。
4.7、WebSocket.onmessage
message 事件会在 WebSocket 接收到新消息时被触发。
语法:
ws.onmessage = function(event) {
const data = event.data;
// 处理数据
}
如果要指定多个回调函数,可以使用addEventListener方法。
ws.addEventListener('message', function (event) {
const data = event.data;
// 处理数据
});
注意,服务器数据可能是文本,也可能是二进制数据(blob对象或Arraybuffer对象),可以通过WebSocket.binaryType判断得到。
ws.onmessage = function(event){
if(typeof event.data === String) {
console.log("Received data string");
}
if(event.data instanceof ArrayBuffer){
var buffer = event.data;
console.log("Received arraybuffer");
}
}
5、WebSocket环境搭建
讲到很多WebSocket的用法和好处,那么现在就搭建一个比较原生的WebSocket环境吧!
5.1、客户端与服务器
客户端:就是指的是浏览器了,可以直接使用原生WebSocket的构造函数。
服务器:这里使用Node搭建,当然也可以使用其他的语言。websocketd可以去玩一玩,不限脚本语言。
5.2、服务器
1、使用npm新建一个项目
npm init
2、安装ws库,不了解ws库的,可以看看官方使用文档:ws
npm install ws
3、新建文件wsServer.js
const { WebSocketServer } = require('ws')
// 构造函数
const ws = new WebSocketServer({
port: 8080
})
// 监听连接
ws.on('connection', (ws, req) => {
const clientAddress = req.socket.remoteAddress
console.log('客户端已连接:', clientAddress)
ws.send('客户端,你好!我是服务端!')
ws.on('message', data => {
console.log('收到客户端的信息:', data)
})
})
4、运行
在项目目录中执行以下命令,ws://localhost:8080服务就起来了
node wsServer
5.3、客户端
在浏览器中直接使用WebSocket构造函数
创建一个ws.html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WebSocket</title>
</head>
<body>
<script>
const ws = new WebSocket('ws://localhost:8080')
ws.onopen = () => {
console.log('已连接!')
if (ws.readyState === 1) {
ws.send('你好,服务器!我是客户端!')
}
}
ws.onmessage = (messageEvent) => {
console.log('客户端收到的消息', messageEvent.data)
}
ws.onclose = () => {
console.log('连接关闭!')
}
</script>
</body>
</html>
放在浏览器中打开
服务端的响应
6、从控制台来分析 WebSocket
6.1、WS请求
WS请求:
打开浏览器的控制台,选择Network下面,其中有一个可以过滤请求类型的,这里选中WS,看到下方有一个请求
WS的header:
其中主要Request Headers和Response Headers
WS的Message:
客户端与服务器的消息对话在Message中可以清晰的看到
WS的Timing:
可以看到WS的Timing:request is not finished yet !
6.2、客户端协议升级
报文格式:
WebSocket报文格式,且只支持GET方法
GET ws://localhost:8080/ HTTP/1.1
Host: localhost:8080
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36
Upgrade: websocket
Origin: null
Sec-WebSocket-Version: 13
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh-TW;q=0.9,zh;q=0.8,en-US;q=0.7,en;q=0.6
Sec-WebSocket-Key: v1/kSADNgDv1r2afDX0fbg==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
主要参数解析:
- Connection: Upgrade:表示要升级协议
- Upgrade: websocket:表示要升级到websocket协议。
- Sec-WebSocket-Version: 13:表示websocket的版本。如果服务端不支持该版本,需要返回一个Sec-WebSocket-Versionheader,里面包含服务端支持的版本号。
- Sec-WebSocket-Key:与后面服务端响应首部的Sec-WebSocket-Accept是配套的,提供基本的防护,比如恶意的连接,或者无意的连接。
6.3、服务端协议升级
报文格式:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: 2iLZ72mDoCjQY1UEIRvsKyE4mwU=
主要参数解析:
- 101 Switching Protocols 状态代码
101表示协议切换 - Sec-WebSocket-Accept: 与客户端请求首部的Sec-WebSocket-Key是配套的
7、常用WebSocket的Node库
常用的 Node 实现有以下三种。
有兴趣对Socket.io做一次详解,听说是目前最好用的一款!
总结
其实写到这里,WebSocket的大门才刚刚打开,WebSocket的安全性考虑,特别是实战化的东西往往才能真正体现出WebSocket的魅力。如果只是一味用来替换轮询,那就太遗憾了!
博客说明与致谢
文章所涉及的部分资料来自互联网整理,其中包含自己个人的总结和看法,分享的目的在于共建社区和巩固自己。
引用的资料如有侵权,请联系本人删除!
感谢万能的网络,W3C,菜鸟教程等!
如果你感觉对你有帮助的话,不妨给我点赞鼓励一下,好文记得收藏哟!关注我一起成长!
幸好我在,感谢你来!