写点什么

网络协议之:WebSocket 的消息格式

发布于: 6 小时前

简介我们知道 WebSocket 是建立在 TCP 协议基础上的一种网络协议,用来进行客户端和服务器端的实时通信。非常的好用。最简单的使用 WebSocket 的办法就是直接使用浏览器的 API 和服务器端进行通信。


本文将会深入分析 WebSocket 的消息交互格式,让大家得以明白,websocket 到底是怎么工作的。


WebSocket 的握手流程我们知道 WebSocket 为了兼容 HTTP 协议,是在 HTTP 协议的基础之上进行升级得到的。在客户端和服务器端建立 HTTP 连接之后,客户端会向服务器端发送一个升级到 webSocket 的协议,如下所示:


GET /chat HTTP/1.1Host: example.com:8000Upgrade: websocketConnection: UpgradeSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==Sec-WebSocket-Version: 13


注意,这里的 HTTP 版本必须是 1.1 以上。HTTP 的请求方法必须是 GET


通过设置 Upgrade 和 Connection 这两个 header,表示我们准备升级到 webSocket 了。


除了这里列的属性之外,其他的 HTTP 自带的 header 属性都是可以接受的。


这里还有两个比较特别的 header,他们是 Sec-WebSocket-Version 和 Sec-WebSocket-Key。


先看一下 Sec-WebSocket-Version, 它表示的是客户端请求的 WebSocket 的版本号。如果服务器端并不明白客户端发送的请求,则会返回一个 400 (“Bad Request”),在这个返回中,服务器端会返回失败的信息。


如果是不懂客户端发送的 Sec-WebSocket-Version,服务器端同样会将 Sec-WebSocket-Version 返回,以告知客户端。


这里要特别关注的一个 header 字段就是 Sec-WebSocket-Key。我们接下来看一下这个字段到底有什么用。


当服务器端收到客户端的请求之后,会返回给客户端一个响应,告诉客户端协议已经从 HTTP 升级到 WebSocket 了。


返回的响应可能是这样的:


HTTP/1.1 101 Switching ProtocolsUpgrade: websocketConnection: UpgradeSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=这里的 Sec-WebSocket-Accept 是根据客户端请求中的 Sec-WebSocket-Key 来生成的。具体而言是将客户端发送的 Sec-WebSocket-Key 和 字符串”258EAFA5-E914-47DA-95CA-C5AB0DC85B11″ 进行连接。然后使用 SHA1 算法求得其 hash 值。


最后将 hash 值进行 base64 编码即可。


当服务器端返回 Sec-WebSocket-Accept 之后,客户端可以对其进行校验,已完成整个握手过程。


webSocket 的消息格式之所以要使用 webSocket 是因为 client 和 server 可以随时随地发送消息。这是 websocket 的神奇所在。那么发送的消息是什么格式的呢?我们来详细看一下。


client 和 server 端进行沟通的消息是以一个个的 frame 的形式来传输的。frame 的格式如下:


  0                   1                   2                   3  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-------+-+-------------+-------------------------------+ |F|R|R|R| opcode|M| Payload len |    Extended payload length    | |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           | |N|V|V|V|       |S|             |   (if payload len==126/127)   | | |1|2|3|       |K|             |                               | +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + |     Extended payload length continued, if payload len == 127  | + - - - - - - - - - - - - - - - +-------------------------------+ |                               |Masking-key, if MASK set to 1  | +-------------------------------+-------------------------------+ | Masking-key (continued)       |          Payload Data         | +-------------------------------- - - - - - - - - - - - - - - - + :                     Payload Data continued ...                : + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + |                     Payload Data continued ...                | +---------------------------------------------------------------+
复制代码


MASK 表示的是消息是否是被编码过的,对于从 client 过来的消息来说,MASK 必须是 1。如果 client 发送给 server 端的消息,MASK 不为 1,则 server 需要断开和 client 的连接。但是 server 端发送给 client 端的消息,MASK 字段就不需要设置了。


RSV1-3 是扩展的字段,可以忽略。


opcode 表示怎么去解释 payload 字段。payload 就是实际要传递的消息。0x0 表示继续,0x1 表示文本,0x2 表示二进制,其他的表示控制字段。


FIN 表示是否是消息的最后一个 frame。如果是 0,表示该消息还有更多的 frame。如果是 1 表示,该 frame 是消息的最后一部分了,可以对消息进行处理了。


为什么需要 Payload len 字段呢?因为我们需要知道什么时候停止接收消息。所以需要一个表示 payload 的字段来对消息进行具体的处理。


怎么解析 Payload 呢?这个就比较复杂。


首先读取 9-15 bits,将其解析为无符号整数。如果其小于 125,那么这个就是 payload 的长度,结束。如果是 126,那么就去到第二步。如果是 127,那么就去到第三步。读取下一个 16 bits,然后将其解析为无符号整数,结束。读取下一个 64 bits。将其解析为符号整数。结束。如果设置了 Mask,那么读取下 4 个字节,也就是 32bits。这个是 masking key。当数据读取完毕之后,我们就获取到了编码过后的 payload:ENCODED,和 MASK key。要解码的话,其逻辑如下:


var DECODED = "";for (var i = 0; i < ENCODED.length; i++) {DECODED[i] = ENCODED[i] ^ MASK[i % 4];


FIN 可以和 opcode 一起配合使用,用来发送长消息。


FIN=1 表示,是最后一个消息。 0x1 表示是 text 消息,0x2 是 0,表示是二净值消息,0x0 表示消息还没有结束,所以 0x0 通常和 FIN=0 一起使用。


Extensions 和 Subprotocols 在客户端和服务器端进行握手的过程中,在标准的 websocket 协议基础之上,客户端还可以发送 Extensions 或者 Subprotocols。这两个有什么区别呢?


首先这两个都是通过 HTTP 头来设置的。但是两者还是有很大的不同。Extensions 可以对 WebSocket 进行控制,并且修改 payload,而 subprotocols 只是定义了 payload 的结构,并不会对其进行修改。


Extensions 是可选的,而 Subprotocols 是必须的。


你可以将 Extensions 看做是数据压缩,它是在 webSocket 的基础之上,对数据进行压缩或者优化操作,可以让发送的消息更短。


而 Subprotocols 表示的是消息的格式,比如使用 soap 或者 wamp。


子协议是在 WebSocket 协议基础上发展出来的协议,主要用于具体的场景的处理,它是是在 WebSocket 协议之上,建立的更加严格的规范。


比如,客户端请求服务器时候,会将对应的协议放在 Sec-WebSocket-Protocol 头中:


GET /socket HTTP/1.1...Sec-WebSocket-Protocol: soap, wamp 服务器端会根据支持的类型,做对应的返回,如:


Sec-WebSocket-Protocol: soap 总结本文讲解了 webSocket 消息交互的具体格式,可以看到很多强大功能的协议,都是由最最基本的结构组成的。


本文已收录于 http://www.flydean.com/07-websocket-message/


最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!


欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

发布于: 6 小时前阅读数: 4
用户头像

关注公众号:程序那些事,更多精彩等着你! 2020.06.07 加入

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧,尽在公众号:程序那些事!

评论

发布
暂无评论
网络协议之:WebSocket的消息格式