从基本用法,以及构成方面详细讲解下,什么是websocket
1. 什么是WebSocket
1.1 定义
- WebSocket API规范定义了一个API用以在网页浏览器以及服务端建立一个socket连接。通俗的来讲就是“在客户端以及服务器端保有一个持久的连接,两边可以在任意时间发送数据”
- HTML5 开始提供的一种浏览器与服务器进行全双工通信的网络技术
- 属于应用层协议,它基于TCP传输协议,并复用了http的握手通道
1.2 优势
- 支持双向通信,实时性更强
- 更好的二进制支持
- 较少的控制开销。连接创建后,ws客户端,服务端进行数据交换时,协议控制的数据包大小更小
1.3 概念补充- 全双工/ 半双工/ 单工
本来这样想画图来解释,但是画图也是比较单调的,简单几个解释还是选择用文字来描述 前提:client端 - 客户端 server端 - 服务端
- 全双工 > 同一时刻client端可以向server端发送请求,server可以向client端发送请求
- 半双工 > 同一时刻只能是client端向server发送请求,或是server向client端进行发送,不能同时进行
- 单工 > 永远都是client端向server发送请求,或是server向client端发送请求
2. WebSocket基本用法
本次演示的代码参照简单使用WebSocket 代码中使用了插件express, 以及ws等
2.1 代码演示
// server端代码
const express = require('express')
const WebSocketServer = require('ws').Server
const app = express()
app.use(express.static(__dirname))
app.listen(3000, () => {
console.log('服务启动成功')
})
const wsServer = new WebSocketServer({ port: 8000 })
wsServer.on('connection', function (socket) {
socket.on('message', function (message) {
console.log('来自客户端消息:' + message)
socket.send('我是服务器端')
})
})
// client端
<!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 Client端</title>
</head>
<body>
<script>
const socket = new WebSocket('ws://localhost:8000')
socket.onopen = function() {
console.log('连接成功')
socket.send('<客户端> 这是我的消息啊')
}
socket.onmessage = function(msg) {
console.log('收到服务器的响应 ' + msg.data);
}
</script>
</body>
</html>
2.2 request 以及response解析
{
"Connection": "Upgrade",
"Sec-WebSocket-Key": "X9WkmK/eC1+uyNKtFACAVA==",
"Sec-WebSocket-Version": "13",
"Upgrade": "websocket"
}
- Connection: Upgrade:表示要升级协议
- Upgrade: websocket:表示要升级到websocket协议
- Sec-WebSocket-Version: 13:表示websocket的版本
- Sec-WebSocket-Key:与后面服务端响应首部的Sec-WebSocket-Accept是配套的,提供基本的防护,比如恶意的连接,或者无意的连接
{
"Connection": "Upgrade",
"Sec-WebSocket-Accept": "y7MZ3tGBc/fYXQBCvmivY9hdrCg=",
"Upgrade": "websocket"
}
- 表示协议切换成功,之后的协议都使用WebSocket协议
2.3 Sec-WebSocket-Accept的计算
const crypto = require('crypto')
const number = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
const webSocketKey = 'mFDWtEYufMK2nefc+ky6Ag=='
const websocketAccept = crypto
.createHash('sha1')
.update(webSocketKey + number)
.digest('base64')
console.log(websocketAccept)
- Sec-WebSocket-Accept根据客户端请求首部的Sec-WebSocket-Key计算出来
- 将Sec-WebSocket-Key跟258EAFA5-E914-47DA-95CA-C5AB0DC85B11拼接
- 通过SHA1计算出摘要,并转成base64字符串
2.4 Sec-WebSocket-Key/Accept的作用
- 避免服务端收到非法的websocket连接
- 确保服务端理解websocket连接
- 用浏览器里发起ajax请求,设置header时,Sec-WebSocket-Key以及其他相关的header是被禁止的
- Sec-WebSocket-Key主要目的并不是确保数据的安全性,因为Sec-WebSocket-Key、Sec-WebSocket-Accept的转换计算公式是公开的,而且非常简单,最主要的作用是预防一些常见的意外情况(非故意的)
2.5 数据帧格式
- 单位是比特。比如FIN、RSV1各占据1比特,opcode占据4比特
- FIN:1个比特 如果是1,表示这是消息(message)的最后一个分片(fragment),如果是0,表示不是是消息(message)的最后一个分片(fragment)
- RSV1, RSV2, RSV3:各占1个比特。一般情况下全为0,一般是用来扩展的
- Opcode: 4个比特。操作代码,Opcode的值决定了应该如何解析后续的数据载荷(data payload)。
- %x0:表示一个延续帧。当Opcode为0时,表示本次数据传输采用了数据分片,当前收到的数据帧为其中一个数据分片
- %x1:表示这是一个文本帧(frame)
- %x2:表示这是一个二进制帧(frame)
- %x3-7:保留的操作代码,用于后续定义的非控制帧
- %x8:表示连接断开
- %x9:表示这是一个ping操作
- %xA:表示这是一个pong操作
- %xB-F:保留的操作代码,用于后续定义的控制帧
- Mask: 1个比特。表示是否要对数据载荷进行掩码操作
- 从客户端向服务端发送数据时,需要对数据进行掩码操作;从服务端向客户端发送数据时,不需要对数据进行掩码操作,如果服务端接收到的数据没有进行过掩码操作,服务端需要断开连接
- 如果Mask是1,那么在Masking-key中会定义一个掩码键(masking key),并用这个掩码键来对数据载荷进行反掩码。所有客户端发送到服务端的数据帧,Mask都是1
- Payload length:数据载荷的长度,单位是字节。为7位,或7+16位,或7+64位
- Masking-key:0或4字节(32位) 所有从客户端传送到服务端的数据帧,数据载荷都进行了掩码操作,Mask为1,且携带了4字节的Masking-key。如果Mask为0,则没有Masking-key。载荷数据的长度,不包括mask key的长度
3. 项目实战
这里介绍个项目TODO多人在线平台, 实现了多人在线建立TODO笔记,支持多人同步编辑以及添加好友等,关于webSocket等功能后续完善中…
|