WebSocket是HTML5开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。在WebSocket API中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。
浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据。当你获取 Web Socket 连接后,你可以通过 send() 方法来向服务器发送数据,并通过 onmessage 事件来接收服务器返回的数据。
是什么
一个网络协议,可实现客户端与服务端间的全双工通信,即支持服务端自主推送,同时还支持跨域
基于 HTTP协议完成握手
有什么作用
主要是为了提高效率,减少带宽
在 websocket 出现之前,开发实时 web 应用的方式为轮询
不停地向服务器发送 HTTP 请求,问有没有数据,有数据的话服务器就用响应报文回应。如果轮询的频率比较高,那么就可以近似地实现“实时通信”的效果 轮询的缺点也很明显,反复发送无效查询请求耗费了大量的带宽和 CPU资源
如何使用
通过 onopen、onmessage、onclose、onerror 四个事件的实现来处理 websocket 的响应
连接 websocket 后,可以通过 send() 方法来向服务器发送数据
与HTTP、TCP的关系
WebSocket和HTTP都属于应用层协议,且都是基于TCP的,它们的send函数最终也是通过TCP系统接口来做数据传输。
WebSocket在建立握手连接时,数据是通过HTTP协议传输的,但是在连接建立后,真正的数据传输阶段则不需要HTTP协议的参与。
握手
websocket协议通信分为两个部分,先是握手,再是数据传输。
客户端发送数据格式
1 | GET /chat HTTP/1.1 |
Connection:必须设置Upgrade,表示客户端希望连接升级
Upgrade:必须设置Websocket,表示希望升级到Websocket协议
Sec-WebSocket-Key:客户端发送的一个 base64 编码的密文,用于简单的认证秘钥。要求服务端必须返回一个对应加密的“Sec-WebSocket-Accept应答,否则客户端会抛出错误,并关闭连接
Sec-WebSocket-Version :表示支持的Websocket版本
服务端返回的数据格式
1 | HTTP/1.1 101 Switching Protocols |
HTTP/1.1 101 Switching Protocols:表示服务端接受 WebSocket 协议的客户端连接
Sec-WebSocket-Accep:验证客户端请求报文,同样也是为了防止误连接。具体做法是把请求头里“Sec-WebSocket-Key”的值,加上一个专用的 UUID,再计算摘要
应用场景
基于websocket的实时通信的特点
- 弹幕
- 媒体聊天
- 协同编辑
- 基于位置的应用
- 体育实况更新
- 股票基金报价实时更新
react 实现聊天室
前端
1 | import { FC, useRef, useState, useEffect } from "react"; |
后端
1 | // let express = require('express') |
心脏与重连机制
目的
客户端和服务端保证彼此还活着,避免丢包发生
连接断开的两种情况
前端断开
比如信号不好,或者网络临时关闭,这时候websocket的连接已经断开,而不同浏览器有不同的机制,触发onclose的时机也不同,并不会理想执行websocket的onclose方法,我们无法知道是否断开连接,也就无法进行重连操作。
后端断开
如果后端因为一些情况需要断开ws,在可控情况下,会下发一个断连的消息通知,之后才会断开,我们便会重连。如果因为一些异常断开了连接,我们是不会感应到的。
心跳机制
通过在指定时间间隔发送心跳包来保证连接正常,如果连接出现问题,就需要手动触发onclose事件,这时候便可进行重连操作。
简单实现
1 | <html> |
1 | let WebSocket = require('ws').Server |
Socket.io
是什么 / 与 websocket 的关系
WebSocket是HTML5最新提出的规范,虽然主流浏览器都已经支持,但仍然可能有不兼容的情况。
为了兼容所有浏览器,给程序员提供一致的编程体验,SocketIO 将 WebSocket、AJAX 和其它的通信方式全部封装成了统一的通信接口。
也就是说,我们在使用SocketIO时,不用担心兼容问题,底层会自动选用最佳的通信方式。
因此说,WebSocket是SocketIO的一个子集。
也就是说,Websocket仅仅是 Socket.io实现实时通信的一个子集。因此Websocket客户端连接不上Socket.io服务端,当然Socket.io客户端也连接不上Websocket服务端。
简单例子
1 | <script src="/socket.io/socket.io.js"></script> |
1 | const app = require('express')(); |