1.WebScoket简介
WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。
WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
浏览器和服务器只需要完成一次握手,两者之间就可以建立持久性的连接,并进行双向数据传输。
2.与HTTP有什么区别
HTTP协议只能实现客户端请求,服务端响应的这种单项通信。
而WebSocket可以实现客户端与服务端的双向通讯。
最大的区别就是可以做到服务端主动将消息推送给客户端。
总而言之:HTML5 定义的 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。
那么为什么要设计WebScoket呢?
也正是HTTP 协议无法实现服务器主动向客户端发起消息,尽管采用轮训调用的方式,效率低、资源消耗大。 WebSocket 连接允许客户端和服务器之间进行全双工通信,以便任一方都可以通过建立的连接将数据推送到另一端。WebSocket 只需要建立一次连接,就可以一直保持连接状态。这相比于轮询方式的不停建立连接显然效率要大大提高。
WebScoket的优势特点

- 握手阶段采用 HTTP 协议。
- 数据格式轻量,性能开销小。客户端与服务端进行数据交换时,服务端到客户端的数据包头只有2到10字节,客户端到服务端需要加上另外4字节的掩码。HTTP每次都需要携带完整头部。
- 更好的二进制支持,可以发送文本,和二进制数据
- 没有同源限制,客户端可以与任意服务器通信
- 协议标识符是ws或wss,请求的地址就是后端支持WebSocket的API。
- 没有同源限制,客户端可以与任意服务器通信
- 协议标识符是ws或wss,请求的地址就是后端支持WebSocket的API。
3.websocket底层实现原理
建立握手-客户端

- Origin:所有浏览器将会发送一个 Origin请求头。 你可以将这个请求头用于安全方面(检查是否是同一个域,白名单/ 黑名单等),如果你不喜欢这个请求发起源,你可以发送一个403 Forbidden。需要注意的是非浏览器只能发送一个模拟的 Origin。大多数应用会拒绝不含这个请求头的请求。
- Sec-WebSocket-Key:是一个 Base64 encode 的值,这个是浏览器随机生成的,告诉服务器:验证是不是真的是Websocket助理
- Sec_WebSocket-Protocol:是一个用户定义的字符串,用来区分同URL下,不同的服务所需要的协议。
- Sec-WebSocket-Version: 是告诉服务器所使用的 Websocket Draft(协议版本)
建立握手-服务端

-
Sec-WebSocket-Key:它通过客户端发送的Sec-WebSocket-Key 计算出来。计算方法:
- 将 Sec-WebSocket-Key 跟 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 拼接;
- 通过 SHA1 计算出摘要,并转成 base64 字符串。
Sec-WebSocket-Key / Sec-WebSocket-Accept 的主要作用还是为了避免一些网络通信过程中,一些非期待的数据包,”乱入“进来,导致一些错误的响应,并不能用于实现登录认证和数据安全,这些功能还需要应用层自己实现。
传输协议
Websocket的数据传递采用frame形式,会将一条消息分为几个frame,按照先后顺序传输出去。这样做会有几个好处:
- 大数据的传输可以分片传输,可以避免数据大小导致的长度标志位不足够的情况。
- 和http的chunk一样,可以边生成数据边传递消息,即提高传输效率。
frame 是客户端和服务端数据传输的最小单元, 当一条消息过长时, 通信方可以将该消息拆分成多个 frame 发送, 接收方收到以后重新拼接、解码从而还原出完整的消息, 在 WebSocket 中, frame 有多种类型, frame 的类型由 frame 头部的 Opcode 字段指示, WebSocket frame 的结构如下所示:

该结构的字段语义如下:
- FIN, 长度为 1 比特, 该标志位用于指示当前的 frame 是消息的最后一个分段, 因为 WebSocket 支持将长消息切分为若干个 frame 发送, 切分以后, 除了最后一个 frame, 前面的 frame 的 FIN 字段都为 0, 最后一个 frame 的 FIN 字段为 1, 当然, 若消息没有分段, 那么一个 frame 便包含了完成的消息, 此时其 FIN 字段值为 1。
- RSV 1 ~ 3, 这三个字段为保留字段, 只有在 WebSocket 扩展时用, 若不启用扩展, 则该三个字段应置为 1, 若接收方收到 RSV 1 ~ 3 不全为 0 的 frame, 并且双方没有协商使用 WebSocket 协议扩展, 则接收方应立即终止 WebSocket 连接。
- Opcode, 长度为 4 比特, 该字段将指示 frame 的类型, RFC 6455 定义的 Opcode 共有如下几种:
- 0x0, 代表当前是一个 continuation frame,既被切分的长消息的每个分片frame
- 0x1, 代表当前是一个 text frame
- 0x2, 代表当前是一个 binary frame
- 0x3 ~ 7, 目前保留, 以后将用作更多的非控制类 frame
- 0x8, 代表当前是一个 connection close, 用于关闭 WebSocket 连接
- 0x9, 代表当前是一个 ping frame
- 0xA, 代表当前是一个 pong frame
- 0xB ~ F, 目前保留, 以后将用作更多的控制类 frame
- Mask, 长度为 1 比特, 该字段是一个标志位, 用于指示 frame 的数据 (Payload) 是否使用掩码掩盖, RFC 6455 规定当且仅当由客户端向服务端发送的 frame, 需要使用掩码覆盖, 掩码覆盖主要为了解决代理缓存污染攻击 (更多细节见 RFC 6455 Section 10.3)。
- Payload Len, 以字节为单位指示 frame Payload 的长度, 该字段的长度可变, 可能为 7 比特, 也可能为 7 + 16 比特, 也可能为 7 + 64 比特. 具体来说, 当 Payload 的实际长度在 [0, 125] 时, 则 Payload Len 字段的长度为 7 比特, 它的值直接代表了 Payload 的实际长度; 当 Payload 的实际长度为 126 时, 则 Payload Len 后跟随的 16 位将被解释为 16-bit 的无符号整数, 该整数的值指示 Payload 的实际长度; 当 Payload 的实际长度为 127 时, 其后的 64 比特将被解释为 64-bit 的无符号整数, 该整数的值指示 Payload 的实际长度。
- Masking-key, 该字段为可选字段, 当 Mask 标志位为 1 时, 代表这是一个掩码覆盖的 frame, 此时 Masking-key 字段存在, 其长度为 32 位, RFC 6455 规定所有由客户端发往服务端的 frame 都必须使用掩码覆盖, 即对于所有由客户端发往服务端的 frame, 该字段都必须存在, 该字段的值是由客户端使用熵值足够大的随机数发生器生成, 关于掩码覆盖, 将下面讨论, 若 Mask 标识位 0, 则 frame 中将设置该字段 (注意是不设置该字段, 而不仅仅是不给该字段赋值)。
- Payload, 该字段的长度是任意的, 该字段即为 frame 的数据部分, 若通信双方协商使用了 WebSocket 扩展, 则该扩展数据 (Extension data) 也将存放在此处, 扩展数据 + 应用数据, 它们的长度和便为 Payload Len 字段指示的值。
4.Websocket属性
| 属性 | |
|---|---|
| Socket.readyState | 只读属性 readyState 表示连接状态,可以是以下值: · 表示连接尚未建立。1表示连接已建立,可以进行通信。2表示连接正在进行关闭。3表示连接已经关闭或者连接不能打开。 |
| Socket.bufferedAmount | 只读属性 bufferedAmount 已被 send() 放入正在队列中等待传输,但是还没有发出的 UTF-8 文本字节数 |
5.Websocket事件
| 事件 | 事件处理顺序 | 描述 |
|---|---|---|
| open | Socket.onopen | 连接建立时触发 |
| message | Socket.onmessage | 客户端接收服务端数据时触发 |
| error | Socket.onerror | 通信发生错误时触发 |
| close | Socket.onclose | 连接关闭时触发 |
6.WebScoket的方法
| 方法 | 描述 |
|---|---|
| Socket.send() | 使用连接发送数据 |
| Socket.close() | 关闭连接 |