本文主要介绍WebSocket协议解决的问题、协议内容等相关知识
WebSocket是为了解决服务端和客户端双向通讯问题,提出的一种传输协议,使客户端和服务端可以互相推送、接收消息,做到真正的双工。
在WebSocket出现之前,往往需要客户端通过频繁的发送HTTP请求,来获取服务端的数据,这会导致一些问题:
如下图所示,WebSocket协议分为握手和数据传输两个阶段。
客户端的握手消息:
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 // 协议版本
Upgrade: websocket
服务端的握手响应消息:
HTTP/1.1 101 Switching Protocols // 101状态代表连接建立成功
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat
Upgrade: websocket
握手过程中,WebSocke连接到服务器的端点,由HTTP的GET请求完成。既允许一个请求地址多个路径,也允许单个IP地址多个端口。WebSocket协议是一个独立的基于TCP的协议。它与HTTP唯一的关系是它的握手是由HTTP服务器解释为一个Upgrade请求。
WebSocket的消息是使用帧序列来传输的,客户端必须使用掩码发送所有的帧。
使用掩码主要是考虑到安全问题,上文传输中提到的Sec-WebSocket-Key,就是编码中使用的,具体编码解码的细节我们在本文中就不介绍了。
一个数据帧各部分定义如下图:
FIN :1bit ,表示是消息的最后一帧,如果消息只有一帧那么第一帧也就是最后一帧。
RSV1,RSV2,RSV3:每个1bit,必须是0,除非扩展定义为非零。如果接受到的是非零值但是扩展没有定义,则需要关闭连接。
Opcode:4bit,解释 Payload 数据,规定有以下不同的状态,如果是未知的,接收方必须马上关闭连接。状态如下:
0x00: 附加数据帧
0x01:文本数据帧
0x02:二进制数据帧
0x3-7:保留为之后非控制帧使用
0x8:关闭连接帧
0x9:ping
0xA:pong
0xB-F(保留为后面的控制帧使用)
Mask:1bit,掩码,定义payload数据是否进行了掩码处理,如果是1表示进行了掩码处理。
Masking-key:域的数据即是掩码密钥,用于解码PayloadData。客户端发出的数据帧需要进行掩码处理,所以此位是1。
Payload_len:7位,7 + 16位,7+64位,payload数据的长度,如果是0-125,就是真实的payload长度,如果是126,那么接着后面的2个字节对应的16位无符号整数就是payload数据长度;如果是127,那么接着后面的8个字节对应的64位无符号整数就是payload数据的长度。
Masking-key:0到4字节,如果MASK位设为1则有4个字节的掩码解密密钥,否则就没有。
Payload data:任意长度数据。包含有扩展定义数据和应用数据,如果没有定义扩展则没有此项,仅含有应用数据。
WebSocket中的帧分为两类:
一条逻辑消息可以分成多个单独的帧。接收端应该对它们进行缓冲,直到设置好fin位。因此,可以将字符串“Hello World”发送到11个包中,每个包的长度为6(报头长度)+ 1字节。控件包不允许分片。但是,规范希望能够处理乱序的控制帧。这是TCP包以任意顺序到达的情况。连接帧的逻辑大致如下:
分片目的是发送长度未知的消息。如果不分片发送,即一帧,就需要缓存整个消息,计算其长度,构建frame并发送;使用分片的话,可使用一个大小合适的buffer,用消息内容填充buffer,填满即发送出去。
主动关闭:发送关闭帧来关闭会话。服务端和客户端都可以主动发送关闭帧。
对于协议更详细的介绍可以参照:WebSocket的RFC文档
Chrome最终展示了每次请求的三个纬度:
其中消息部分,绿色向上箭头表示消息由客户端发送到服务端,红色向下箭头表示消息由服务端发送到客户端。
不过使用Chrome抓包有局限性,看不到全部的帧信息,可以使用Wireshark抓包工具进行抓包。
使用WebSocket协议也会对系统架构造成一些影响。如下图,非WebSocket协议下,我们的业务服务很容易扩展,只要保证服务无状态就可以了。
引入WebSocket后一种比较典型的架构设计,如下图所示:
引入WebSocket后,为了保证服务的可扩展性,我们往往需要做一些分层设计,把WebSocket协议层单独拆分,通过消息队列和业务服务解耦。这样就可以保证业务服务的可扩展性。 总之引入WebSocket会给系统带来复杂性。系统架构的设计,如何保证服务的无状态,广播消息的实现等等。
长链接除了WebSocket外还有一种解决方案:HTTP/2 + SSE,后续有时间再分享。
[1] https://websocket.org/
[2] https://gitee.com/geektime-geekbang/geektime-webprotoc
[3] https://datatracker.ietf.org/doc/rfc6455/
[4]https://segmentfault.com/a/1190000017448270
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8