前端必备 HTTP 技能之 WebSocket 协议详解

3,686 阅读6分钟
原文链接: www.jianshu.com

WebSocket是一个计算机通信协议,通过一个TCP连接提供全双工的通信频道。2011年IETFRFC6455文件中标准化了WebSocket协议,WebSocket的 Web IDL格式的API是W3C标准化的。

WebSocket被设计成需要web浏览器和web服务器共同实现,但是它也可以被任意的客户端或者服务端应用使用。WebSocket 协议是一个独立的基于TCP的协议。和HTTP唯一的关系是它的握手被HTTP服务器识别为Upgrade 请求。WebSocket协议让浏览器和web服务器之间实时数据传输成为可能。也为这种标准方式提供了可能,就是服务端在未经客户端请求时给浏览器发送内容,并且当连接没有关闭,信息可以来回传递。用这种方式,在浏览器和服务器之间可以发生一种双向的会话。这种通信是通过80端口的TCP完成的,这有利于哪些阻塞使用防火墙的非web网络连接的环境。使用应急的技术方案,例如 Comet这种非标准方式也可以获得类似双向的浏览器-服务器互相通信的功能。

目前支持WebSocket协议的浏览器有Chrome,Edge,IE,Firefox,Safari,Opera。WebSocket也需要服务器上的web应用支持才可以。

概述

不像HTTP,WebSocket提供全双工的通信。另外,WebSocket支持基于TCP的消息流。TCP单独处理流字节的时候没有消息的概念。在WebSocket之前,80端口的全双工通信,可以通过Comet方式实现,但是Comet实现起来不容易,因为TCP握手以及HTTP头开销,对于少量消息,这种方式很低效。WebSocket协议的目的就是解决这种问题,在不影响web安全的情况下。

WebSocket协议规范定义了两种新的URI schemes,即ws,wss,分别标示未加密和加密的链接。除了scheme名称和fragment(不支持#)不一样,URI的其它部分和普通URI一样。

使用浏览器的开发者工具,开发者可以检测WebSocket握手以及WebSocket帧。

历史

在HTML5规范中WebSocket第一次被作为TCP链接引用,给基于TCP的socket api占位。在2008年6月,Michael Carter主导了一系列的讨论,然后就出了第一个版本的WebSocket协议。

WebSocket这个名称是Ian Hickson和Michael Carter在IRC网络聊天室协力创造的,随后被Ian Hickson授权在HTML5规范中使用,然后Michael Carter在cometdaily博客上公开宣布了这个名称。在2009十二月,Chrome成为第一个完成支持这个标准的浏览器,并且WebSocket默认启用。随后在2010年WebSocket协议的开发工作从W3C和whatwg组织中交到IETF,Ian Hickson编写了两版修正案。

协议发布并且被大多数浏览器支持之后,在2011年十二月Ian Fette完成了协议的RFC。

浏览器实现

Firefox6,Safari6,Chrome4,Opera12.10,IE10都实现了一个安全版本的WebSocket协议。
详细的浏览器支持参看这里;

协议的握手过程

为了建立WebSocket连接,客户端发送WebSocket握手请求,服务器返回一个WebSocket握手响应,下面的例子展示了这个过程。

客户端请求(和HTTP类似,每行以换行符结束,最后有一个空白行):

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com

服务端响应:

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

握手行为类似于HTTP,这样服务器就可以在相同的端口处理HTTP和WebSocket连接。一旦连接建立,通信就会转成不符合HTTP协议的双向二进制协议。

除了Upgrade头,客户端也发送一个包含base64编码随机字节的Sec-WebSocket-Key头,服务端在Sec-WebSocket-Accept头中以Sec-WebSocket-Key值的散列响应。这主要为了阻止因为重新发送之前WebSocket会话而产生的缓存代理,不提供任何身份验证、隐私或完整性。散列函数在Sec-WebSocket-Key值(不用从base64解码)之后追加一个固定字符串258EAFA5-E914-47DA-95CA-C5AB0DC85B11(GUID),然后应用 SHA-1散列函数,并且利用base64编码结果。

一旦连接被创建,客户端和服务端可以互相以全双工的方式发送WebSocket数据或者文本帧。WebSocket传输也成为消息,一个单独的消息可以被任意分成多个数据帧。这就允许发送哪些初始数据是有效的,但是不知道数据的完整长度的消息(一个数据帧一个数据帧的发送,直到发送完毕,然后以FIN位标记)。根据扩展协议,也可以同时多路复用多个流(例如可以避免独自占据一个socket发送一个大的数据)。

从安全角度来说,在服务端建立连接的过程中校验Origin头是很重要的,这样可以避免跨站点的WebSocket劫持攻击,当连接经过Cookie或者HTTP认证校验之后,这种攻击是可能的。发送敏感数据时,最好使用token或者类似的保护机制来校验WebSocket连接。

代理遍历

实现了WebSocket协议的客户端会尝试检测用户浏览器是否配置了使用代理当连接目标站点时,如果是的话,客户端会使用HTTP的CONNECT方法建立一个固定的通信通道。

虽然WebSocket协议本身不知道代理服务器和防火墙,它通过允许HTTP服务器和WebSocket的网关或者服务器共享HTTP和HTTPS端口(80和443)的方式兼容HTTP握手。WebSocket协议定义ws://wss://前缀分别标示WebSocket连接和WebSocket安全连接。这两种scheme都是使用HTTP的upgrade机制来升级到WebSocket协议。有些代理可以很好的兼容WebSocket;也有一些不能正确支持WebSocket,这就导致连接失败。在有些情况下,可能需要额外的代理服务起配置,某些代理服务器可能需要升级支持WebSocket。

如果未加密的WebSocket信息流经过一个显示或者透明的不支持WebSocket协议的代理服务器,那么连接很有可能失败。

如果使用了一个加密的WebSocket链接,在WebSocket安全连接中要使用TLS确保配置了显示代理服务器的浏览器发出HTTP CONNECT命令。

做好前端开发必须对HTTP的相关知识有所了解,所以我创建了一个专题前端必备HTTP技能专门收集前端相关的HTTP知识,欢迎关注,投稿。


PS:本文翻译自维基百科,原文地址en.wikipedia.org/wiki/WebSoc…


本文对你有帮助?欢迎扫码加入前端学习小组微信群: