WebSocket协议简析

websocket由rfc6455所定义,主要用于在html5上模拟raw socket通信,这个牛逼的feature需要浏览器支持(很显然的)。
websocket是传统的c/s协议,服务端和客户端(通常是浏览器)分别遵循rfc6455的定义进行实现,由浏览器暴露接口给js进行客户端编程(暴露给js的接口用起来很简单,本文就不讨论了)。
根据rfc6455所述(6455的篇幅较为短小,跟以前读过的3720比起来的话。。。),websocket是用来解决browser-based应用需要双向通信的需求。它实际上是一种建立在tcp之上的传输协议(连接建立时会使用HTTP协议进行handshake),所以一个ws连接是占用一个tcp连接的,并且是有状态的。
相较于raw socket来说它有一些很神奇的功能,比如它不会粘包(使用frame来界定消息包),在协议头中表达payload长度的字段是可变的(这使得小包时可以节省一点流量,大包时也能表达,当然它其实是支持分片传输的)。
一个ws连接始于client的handshake:
官方的格式示例如
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
一个实际的格式如下(可以看到在这个handshake之前做了TCP的三路握手建立连接)

然后服务端应答handshake:

官方的格式示例如

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat

可以看到在handshake的过程中报文协议实际上就是HTTP,这个handshake的HTTP报文是可以加上其他字段的,比如cookie。
上述一来一去的handshake完成之后就进入了数据传输阶段。
数据传输阶段使用frame进行通信,frame分不同的类型,主要有:文本数据,二进制数据,控制信令,其中文本数据和二进制数据都是有实际荷载(payload)的frame。
应用需要的数据单元message,由一个或多个frame进行传递。
官方frame的格式如下:
 
一个实际的frame的格式如下(客户端发给服务端的):



其中FIN(Finish)域表明这个frame是否表达完一个message(或者控制信令),其中0为未完(一个message分为多个frame传输),1为完
多个frame传输一个message的表达方式有点意思,组合使用opcode和FIN.
让第一个frame的FIN为0,opcode非0,中间的frame的FIN为0,opcode为0,最后结束的frame的FIN为1,opcode为0

另外一个值得小讨论一下的问题是frame荷载大小表达的问题。上图wireshark中的frame我特地用了一个很奇怪的payload长度:126.
为什么126是个奇怪的长度?从官方格式可以看出payload len本来是只能用7bit来表达的,也就是最多一个frame的payload只能有127个字节,6455给出的解决方案是添加扩展payload len字段。
当payload实际长度超过126(包括),但在2^16-1长度内,则将payload len置为126,payload的实际长度由长为16bit的extended payload length来表达。(所以上图出现了payload len和extended payload length都为126的奇怪表达方式,哈哈,本来可以只用payload len一个字节表达的,结果变为了3个字节)
当payload实际长度超过2^16(包括),但在2^64-1长度内(呵呵。。真有这么长的数据包么??),则将payload置为127,payload的实际长度由长为64bit的extended payload length来表达。

最后,留个小问题,127长度的payload会如何表达??。。。

你可能感兴趣的:(Protocols,Network)