WebSocket协议详解及应用(二)-WebSocket握手协议之浏览器请求

目前WebSocket协议版本已更新到13,本系列文章均以WebSocket 13版本为例。

一、通过WebSocket API与服务器进行握手

WebSocket的构造方法中有两个参数,其原型如下:WebSocket(url, [protocols]),url为需要连接的地址。WebSocket的协议头的写法有2个,一个是ws://,另一个是wss://,它们的区别就是后者相当于https,是加密的。如果url没有加端口号,当协议头为ws时默认为80端口,当协议头为wss时默认为443端口。如果需要连接其他端口,则需要向http协议一样,加上端口号,如ws://localhost:12345。url其他部分与标准URL一致,以下URL是合法的:ws://localhost:12345/path/?queryString=queryString。具体URL标准将在下面协议详解中详细说明。
第二个参数protocols为可选参数,代表WebSocket协议的子协议(可以是用户自定义的)。你可能已经注意到了第二个参数名为protocols最后有个s,它可以是一个字符串亦可以是一个数组,如果protocols是一个字符串,则它等价于一个单一值的数组。例如,
WebSocket(‘ws://localhost’,’subprotocol’)
等价于
WebSocket(‘ws://localhost’,[‘subprotocol’])

二、WebSocket URL解析

以下内容部分翻译自W3C规范

WebSocket URL解析组件解析URL步骤如下,这些步骤会返回一个主机名、端口号、资源名和一个安全标识符否则返回失败:

  1. 如果URL不是一个绝对URL,算法失败
  2. 将URL字符串转化为UTF-8格式
  3. 如果URL的模式转为小写(模式是不区分大小写的,即WS、WSS都是可以的)不是ws或wss,则算法失败
  4. 如果解析的URL结果中存在fragment(#后面的),则算法失败
  5. 如果是ws模式的URL,则将安全标识符设为false,如果是wss模式的URL则将安全标示符置为true
  6. 解析url的host名,并转为小写
  7. 如果有端口号则设置端口号,否则使用隐式端口号声明
  8. 如果端口号是隐式声明的,当安全标示符为true时,端口号是443,否则为80
  9. url的path作为资源名,可以是空字符串
  10. 如果资源名是非空的,则将其设置到结果中,否则将/作为资源名
  11. 如果URL请求字符串(?后面的内容)非空,则将请求字符串加入结果中,并使用?拼接(同HTTP中URL的请求字符串写法)
  12. 将主机名、端口号、资源名和安全标示符返回

三、浏览器发送握手包

通过本文第一部分我们可知,浏览器端发送握手请求的API非常简单,只需要new出一个WebSocket的对象,最少情况只需要带一个参数,如var ws = new WebSocket(‘ws://localhost’);
我们在http://dev.w3.org中打开控制台使用var ws = new WebSocket(‘ws://localhost’,’test’);浏览器会发出类似如下请求(注意:这里不会存在跨域问题,不需要在同域下即可,因为WebSocket是在TCP层面上传输数据):

GET ws://localhost/ HTTP/1.1
Host: localhost
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
Origin: http://dev.w3.org
Sec-WebSocket-Version: 13
Sec-WebSocket-Protocol:test
User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 Safari/537.36
Accept-Encoding: gzip,deflate,sdch
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.6,en;q=0.4
Sec-WebSocket-Key: 32pdAhmqFrFZik/MP7fU8A==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits

其实这个就是一个标准的HTTP协议,WebSocket协议中的握手过程是HTTP协议,而一旦握手成功后,就不再是HTTP协议,而是直接通过TCP传输数据。
请求头中大部分内容与HTTP协议一致,这里不再解释,只解释那些不曾出现在普通请求的请求头。
Connection: Upgrade是固定的,表示需要转换为其他协议
Upgrade: websocket是固定的,说明要转换成的协议时WebSocket
Sec-WebSocket-Version: 13代表WebSocket的版本,目前版本是13
Sec-WebSocket-Key: 32pdAhmqFrFZik/MP7fU8A==这个是握手的认证串,服务端需要将此key进行一定处理后返回,再由浏览器验证有效性,必须符合算法结果才可正常建立连接(只是简单的哈希计算)
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits代表客户端支持的扩展类型
Sec-WebSocket-Protocol:test为子协议,是否存在取决于构造方法的第二个参数,如果第二个参数是个数组,则此处的值为数组按一个逗号和一个空格分隔,相当于[].join(‘, ‘);
其中Sec-WebSocket-Key是非常重要的请求头,我们会在服务端处理这个请求头,只有处理正确,浏览器才会正确的与服务器建立连接。

WebSocket协议详解及应用(一)-初识WebSocket

本系列文章主要介绍WebSocket协议以及基于该协议的服务器端、客户端编程。服务器端代码采用的是PHP语言开发,Nginx做代理负责代理客户端与PHP脚本之间的数据通信;客户端语言为JavaScript,测试浏览器为Chrome。

一、什么是WebSocket

WebSocket是一个允许Web应用程序(通常指浏览器)与服务器进行双向通信的协议。HTML5的WebSocket API主要是为浏览器端提供了一个基于TCP协议实现全双工通信的方法。该技术的目标是为基于浏览器的双向通信应用提供一个不需要打开多个HTTP连接的机制。

二、优点

1.全双工通信
在传统的Web应用中,浏览器与服务器交互都是半双工通信(但并不完全是半双工通信,服务器无法主动向浏览器推送)。即同一时间内数据流向是单一的,浏览器向服务器发送请求后需要等待服务器返回数据。
而在WebSocket中,浏览器和服务器之间可随时进行通信,不必等待对方传送完毕,浏览器接收到服务器的数据后会自动触发onmessage事件。
2.实时性
传统的Web应用很难做到实时通信,通常是用长连接或轮询的方式进行。对于服务器来说,轮询法是被动传输数据,即使数据有更新,但浏览器还未发送请求,则消息无法进行实时推送。
3.更少的数据传输及更少的请求数
传统Web应用中浏览器与服务器进行数据交互通常需要经过以下几个步骤:

  1. DNS查询
  2. TCP三次握手
  3. 传送HTTP请求头
  4. 传送HTTP请求体(如果有)
  5. 服务器处理后传送响应头
  6. 服务器传送响应体
  7. 断开TCP连接

在WebSocket中进行交互通常为以下几个步骤:

  1. DNS查询
  2. TCP三次握手
  3. WebSocket握手
  4. 浏览器发送请求
  5. 服务器发送响应
  6. 断开TCP连接

从上面可以看到如果仅是一次通信,二者差异并不是很大,甚至WebSocket比普通方式还要多一次握手。但在需要频繁交互数据时,WebSocket的优势就显露出来了。
例如,当有10次数据交互时,前者最多可能要建立10个TCP连接,然后要发送10次请求头(包含Cookie等信息,数据量可能会达到KB级别),接收的响应信息可能才几个字节(如某些心跳包、空消息等),这样会极大的浪费带宽等资源。试想,如果你在做一个聊天应用,想要获取当前在线人数,你需要向服务器发送你的全部cookie(至少要几百个字节),除此之外HTTP头中还要包含其他信息,如URL、host等,这些都是必不可少的。最后服务器返回了几百个字节,但其中真正需要用到的只有不到10字节(只需要知道在线人数,其他信息都是无用的)。通过WebSocket,浏览器可以向服务器发送1~2字节的请求(不需要带上cookie验证身份,可以在握手时进行认证,一旦TCP连接建立,则在连接上的通信都是认证过身份的数据,这也是它的好处之一:便于服务端识别客户端的状态),这个请求仅包含一个特定的控制码(由开发者实现的应用层协议指定),服务器只需返回特定的返回码及数据即可,一切无用的字节都被省去。

三、应用场景
1.数据传输实时性要求较高
Web聊天室、直播实时弹幕、HTML5网络互动类游戏
2.推送类应用
网站消息通知、邮箱新邮件提醒
3.监控在线状态、精确统计在线时长
统计用户行为
4.远程调试代码、云指令系统
部分移动端调试工具是基于WebSocket开发(此处为WebSocket协议而非WebSocket API)
5.其他用途
网络(包括内网)嗅探(端口扫描)、僵尸网络及后门(云指令系统)

四、浏览器支持
IE:10+
Firefox:4+(11+完全支持)
Chrome:4+(16+完全支持)
Safari:5+(7+完全支持)
Opera:11.5+(12.1+完全支持)
Safari(iOS):4.3+(6.1+完全支持)
Android:4.4+
以上数据来源于caniuse.com

五、相关文档
WebSocket rfc6455
WebSocket WHATWG
WebSocket W3C(见WHATWG)