历史上地(从以前开始)创建一个可以使客户端和服务端进行双向通信需要利用HTTP请求轮询服务器才能获取更新的信息
- 服务器被被强制使用底层的TCP连接来维护每个客户端,一个连接用来发送信息到客户端,新的连接用于到达的新消息
- HTTP请求负担太重了,每个客户端的请求都包含了HTTP的请求头
- 客户端需要维护从发送的请求与返回的答复的映射关系
WebSocket支持HTTP的默认端口 80 和 443 支持HTTP代理服务器,它的设计并不局限于HTTP请求,未来的握手方式可能会用一个专有的端口,但是不会去重新设计一个完整的协议
WebSocket 协议分为两部分 1.握手 2.数据传输
客户端握手协议
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Request-URI 如果存在URI 时,会存在于 Sec-WebSocket-Protocol 子协议中; 为了保证服务端收到客户端发送的握手消息 Sec-WebSocket-Key , 服务端通过一系列加密编解码运算返回 Sec-WebSocket-Accept ; Origin 保护未授权的跨域请求,防止攻击;
服务端返回协议的报文:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat
客户端/服务端都可以发送Close Frame (包含具体控制序列的消息) 来开始关闭连接的握手; 通过发送 Close Frame 和等待 Close Frame 的返回为了避免数据的不必要的丢失,在一些平台上 socket 关闭接受队列也同时关闭,导致另一端正在发送的数据读取失败; 关闭握手类似于 TCP (FIN/ACK) 的四次挥手关闭
WebSocket 被设计成一种有最小帧数据的规则,这种规则使协议基于Frame 而不是基于流,可以使用 unicode 文本 或者 二级制帧数据;
WebSocket 协议握手消息只是比 HTTP请求多一些 SEC 开头的请求头
- 独立的基于TCP 的协议,跟HTTP请求的区别也只是使用了HTTP请求进行了握手,升级请求
ws-uri : ws://host : port / path ? query
wss-uri : wss://host : port / path ? query
支持多版本的 WebSocket Sec-WebSokcet-Version 头部
客户端发送协议版本号,服务端支持版本号则接受这个版本协议进行处理,若不支持,服务器端返回支持版本(400) ,客户端重新使用了新版本协议
通过 opcodes 进行区分
0x8 Close
0x9 Ping
0xA Pong
0xB ~ 0xF 保留的状态码
opcode 0x8 Close Frame 可以包含应用数据的消息体,也可以设置关闭原因 (reason),可能是UTF-8编码的数据,也可以是2字节的整型数据用来表示状态值
客户端/服务端发完 Close Frame 的数据之后,不应该在发送任何的数据
如果后端接受 Close Frame 且之前未发送 Close Frame ,那后端必须再发一个 Close Frame 作为回应,发送返回值后,后端将改变一个状态值 (readsytate = 2 Closing) Close Frame 可能会延迟到达,因为目前发送消息队列中可能还存在发送消息;
经过了一接一发的 Close Frame 后端开始进行 TCP 关闭握手
服务端必须关闭 TCP 连接,客户端应等服务端关闭连接,但可能在接受和发送完 Close Frame 的任何时候关闭连接,前提是在可接受的时间段内未接收到服务端发送的 TCP close 的消息;
opcode 0x9
Ping Frame 可能包含应用数据,一收到Ping Frame 的一段必须发送 Pong Frame 作为返回 (可以用来进行心跳),除非已经收到了Close Frame ,Ping 消息,可以在建立连接成功的任何时候进行发送
Ping 消息可以作为保活方式也可以用来确认对端是否存活
opcode 0xA
Pong Frame 用来回复Ping Frame 可以作为心跳回复, 对于接受到 Pong 消息的端,不需要进行回复
同样是用 opcode 来区分
目前定义的 data Frame 包括
0x1 Text
0x2 二进制数据 Binary
Text 是 UTF-8 编码数据
Binary 二进制数据
IDL 接口描述
[Constructor(DOMString url, optional (DOMString or DOMString[]) protocols)]
interface WebSocket : EventTarget {
readonly attribute DOMString url;
// ready state
const unsigned short CONNECTING = 0;
const unsigned short OPEN = 1;
const unsigned short CLOSING = 2;
const unsigned short CLOSED = 3;
readonly attribute unsigned short readyState;
readonly attribute unsigned long bufferedAmount;
// networking
attribute EventHandler onopen;
attribute EventHandler onerror;
attribute EventHandler onclose;
readonly attribute DOMString extensions;
readonly attribute DOMString protocol;
void close([Clamp] optional unsigned short code, optional DOMString reason);
// messaging
attribute EventHandler onmessage;
attribute DOMString binaryType;
void send(DOMString data);
void send(Blob data);
void send(ArrayBuffer data);
void send(ArrayBufferView data);
};
通过页面 js 构造函数
var ws = new WebSokcet (url , protocols );
则websocket 连接创建成功 readystate = 1 ; bufferedAmount 返回当前在发送队列中的数据的字节数
send 方法发送前可以判断一下 bufferdAmount 的数据为0 则进行发送
当 readystate 为 0 时 会抛出异常 INVALID_STATE_ERR 的异常
客户端必须执行精确步骤当webSocket 对象被创建, binaryType 属性被设置成 blob ,接受的数据类型必须与设置的数据类型一直,否则返回 SYNTAL_ERR
- readystate -> open(1)
- extensions 设置
- protocol 属性设置
- 参加梅河口am瑟吉欧 set-cookie 属性的处理
- open事件的触发 (onopen)
- readystate 不是 open (1) 或者 closing (2) 则执行完毕;
- 触发message 事件 (onmessage)
- 初始化 origin 属性
- 解析数据
- 调度 websocket 对象的事件
- readystate -> closed (3)
- 触发error 事件 (onerror)
- 创建 close 事件,触发close 事件 (onclose) ,并带着 事件code 和 reason ,属性wasClean 代表连接是否完全关闭