Websocket协议的简单介绍
Websocket是一种在单个TCP连接上进行全双工通讯的协议,双工(duplex)是指两台通讯设备之间,允许有双向的资料传输。全双工的是指,允许两台设备间同时进行双向数据传输。这是相对于半双工来说的,半双工不能同时进行双向传输,这期间的区别相当于手机和对讲机的区别,手机在讲话的同时也能听到对方说话,对讲机只能一个说完另一个才能说。
长话短说,在Websocket协议中,客户端和服务端只需要做一个握手的动作,就能形成一条通道,两者之间可以进行数据互相传送。
所以WebSocket协议分为两部分:
- 握手
- 数据传输
握手协议
- WebSocket 是独立的、创建在TCP上的协议。
- Websocket 通过 HTTP/1.1 协议的
101状态码进行握手。 - 为了创建Websocket连接,需要通过浏览器发出请求,之后服务器进行回应,这个过程通常称为“握手”(Handshaking)。
优点
- 较少的控制开销。在连接创建后,服务器和客户端之间交换数据时,用于协议控制的数据包头部相对较小。在不包含扩展的情况下,对于服务器到客户端的内容,此头部大小只有2至10字节(和数据包长度有关);对于客户端到服务器的内容,此头部还需要加上额外的4字节的掩码。相对于HTTP请求每次都要携带完整的头部,此项开销显著减少了。
- 更强的实时性。由于协议是全双工的,所以服务器可以随时主动给客户端下发数据。相对于HTTP请求需要等待客户端发起请求服务端才能响应,延迟明显更少;即使是和Comet等类似的长轮询比较,其也能在短时间内更多次地传递数据。
- 保持连接状态。与HTTP不同的是,Websocket需要先创建连接,这就使得其成为一种有状态的协议,之后通信时可以省略部分状态信息。而HTTP请求可能需要在每个请求都携带状态信息(如身份认证等)。
- 更好的二进制支持。Websocket定义了二进制帧,相对HTTP,可以更轻松地处理二进制内容。
- 可以支持扩展。Websocket定义了扩展,用户可以扩展协议、实现部分自定义的子协议。如部分浏览器支持压缩等。
- 更好的压缩效果。相对于HTTP压缩,Websocket在适当的扩展支持下,可以沿用之前内容的上下文,在传递类似的数据时,可以显著地提高压缩率。[14]
握手
客户端发送一个请求
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
服务器回应
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat
1. 在这里我们看一下,Upgrade这个http首部,它的作用是什么呢?
首部字段 Upgrade 用于检测 HTTP 协议及其他协议是否可使用更高的版本进行通信,其参数值可以用来指定一个完全不同的通信协议。
所以在这里我们看到Upgrade直接指定了websocket协议.
2. 那么细心的同学可能又注意到了Connection: Upgrade这个设置。可能有人会心生疑问如果是仅仅Upgrade的话,Connection头部不就是多余的设计了么?
Connection的起源
最开始,在HTTP/1.0出现没多久,人们就意识到HTTP持久连接的重要性(毕竟三次握手还是很慢的),所以各个服务器实现都采用了Keep-Alive头部来表示这个请求支持连接持久化。
HTTP/1.1中的Connection
在HTTP/1.1中,正式标准化了Connection头部:
Connection头部一般表示那些头部是属于逐跳头部的,比如Connection: Custom-Header,就表示在这个连接中,Custom-Header是一个逐跳头部,不应当被代理原样传递给upstream。
有两个例外:close表示会话不持久化,keep-alive表示会话支持持久化(虽然有一个Keep-Alive头部,但是大小些不一样)。
上面,我们提到了逐跳头部:
逐跳头部(hop-by-hop header),用来描述当前浏览器与直连服务器(比如Nginx反向代理)的连接信息。比如Keep-Alive头部,仅仅表示浏览器尝试和Nginx之间连接持久化,而不管Nginx和后端服务器之间的连接。proxy要处理这些头部,并按照自己的需要来修改这些头部。默认的逐条头部如下:
- Connection
- Keep-Alive
- Proxy-Authenticate
- Proxy-Authorization
- TE
- Trailers
- Transfer-Encoding
- Upgrade
其他的头部都是端到端头部(end-to-end header),用来描述这个浏览器和最终处理请求的服务器之间的信息,比如Accept头部,表示客户端想从后端服务器得到的数据类型,而和中间的Nginx无关。proxy不能修改这些头部。
再回到HTTP 1.1的Connection头部,这儿有一个兼容性问题:我们以Upgrade头部为例,某个proxy实现了HTTP 1.0协议,将Upgrade原样转发给后端,后端和proxy升级协议,但是这个情况下,proxy不认识升级后的协议啊。
所以,RFC有增加了一条规定:
如果只有Upgrade: xxx,而没有Connection: Upgrade ,那么就当作普通请求来处理。
所以在websocket协议中,Connection必须设置Upgrade,表示客户端希望连接升级。
3. Sec-WebSocket-Key 和 Sec-WebSocket-Accept
Sec-WebSocket-Key是一个 Base64 encode 的值,这个是浏览器随机生成的。Sec-WebSocket-Accept用在websocket开放握手中。是随机的字符串,服务器端会用这些数据来构造出一个SHA-1的信息摘要。把“Sec-WebSocket-Key”加上一个特殊字符串“258EAFA5-E914-47DA-95CA-C5AB0DC85B11”,然后计算SHA-1摘要,之后进行Base64编码,将结果做为“Sec-WebSocket-Accept”头的值,返回给客户端。客户端收到Sec-WebSocket-Accept后,将本地的Sec-WebSocket-Key进行同样的编码,然后比对。如此操作,可以尽量避免普通HTTP请求被误认为Websocket协议。它会出现在响应头中。 也就是说,这是由服务器发送到客户端的头(header),用以告知服务器愿发起一个websocket连接。
4. 其他相关的
Sec_WebSocket-Protocol是一个用户定义的字符串,用来区分同URL下,不同的服务所需要的协议。简单理解:今晚我要服务老王,别搞错啦~Sec-WebSocket-Version表示支持的Websocket版本。RFC6455要求使用的版本是13,之前草案的版本均应当弃用
数据传输
后续更新。。。