01-ws框架测试
1 websocket简介
websocket是一种网络传输协议,可在单个tcp链接上进行全双工通信,位于OSI模型的应用层。
WebSocket 与 HTTP/2 一样,都是为了解决 HTTP 某方面的缺陷而诞生的。HTTP/2 针对的是“队头阻塞”,而 WebSocket 针对的是“请求 - 应答”通信模式。
WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就可以创建持久性的连接,并进行双向数据传输。
WebSocket协议在2011年由IETF标准化为RFC 6455,后由RFC 7936补充规范。
- RFC6455
- RFC7936
WebSocket是一种与HTTP不同的协议。两者都位于OSI模型的应用层,并且都依赖于传输层的TCP协议。 虽然它们不同,但RFC 6455规定:“WebSocket设计为通过80和443端口工作,以及支持HTTP代理和中介”,从而使其与HTTP协议兼容。 为了实现兼容性,WebSocket握手使用HTTP Upgrade头[1]从HTTP协议更改为WebSocket协议。
WebSocket协议支持Web浏览器(或其他客户端应用程序)与Web服务器之间的交互,具有较低的开销,便于实现客户端与服务器的实时数据传输。 服务器可以通过标准化的方式来实现,而无需客户端首先请求内容,并允许消息在保持连接打开的同时来回传递。通过这种方式,可以在客户端和服务器之间进行双向持续对话。 通信通过TCP端口80或443完成,这在防火墙阻止非Web网络连接的环境下是有益的。另外,Comet之类的技术以非标准化的方式实现了类似的双向通信。
1.1 websocket特点
websocket的特点如下所示:
- 提供全双工通信;
- 还可以在TCP之上启用消息流;
- WebSocket协议规范将
ws
(WebSocket)和wss
(WebSocket Secure)定义为两个新的统一资源标识符(URI)方案,分别对应明文和加密连接。 - WebSocket 的默认端口也选择了 80 和 443,因为现在互联网上的防火墙屏蔽了绝大多数的端口,只对 HTTP 的 80、443 端口“放行”,所以 WebSocket 就可以“伪装”成 HTTP 协议,比较容易地“穿透”防火墙,与服务器建立连接。
- WebSocket 更侧重于“实时通信”,而 HTTP/2 更侧重于提高传输效率。
1.2 服务器支持框架
在服务器方面,网上都有不同对websocket支持的服务器:
- php – http://code.google.com/p/phpwebsocket/
- jetty – https://www.eclipse.org/jetty/(版本7开始支持websocket)
- netty – http://www.jboss.org/netty
- ruby – http://github.com/gimite/web-socket-ruby
- Kaazing – https://web.archive.org/web/20100923224709/http://www.kaazing.org/confluence/display/KAAZING/Home
- Tomcat – http://tomcat.apache.org/(7.0.27支持websocket,建议用tomcat8,7.0.27中的接口已经过时)
- WebLogic – http://www.oracle.com/us/products/middleware/cloud-app-foundation/weblogic/overview/index.html(12.1.2开始支持)
- node.js – https://github.com/Worlize/WebSocket-Node
- node.js – http://socket.io
- nginx – http://nginx.com/
- mojolicious – http://mojolicio.us/
- python – https://github.com/abourget/gevent-socketio
- Django – https://github.com/stephenmcd/django-socketio
- erlang – https://github.com/ninenines/cowboy.git
1.3 websocket 帧结构
“结束标志位 + 操作码 + 帧长度 + 掩码”
- 第一位“FIN”:相当于 HTTP/2 里的“END_STREAM”,表示数据发送完毕。一个消息可以拆成多个帧,接收方看到“FIN”后,就可以把前面的帧拼起来,组成完整的消息。
- “FIN”后面的三个位是保留位,目前没有任何意义,但必须是 0。
- “Opcode”,操作码:其实就是帧类型,比如 1 表示帧内容是纯文本,2 表示帧内容是二进制数据,8 是关闭连接,9 和 10 分别是连接保活的 PING 和 PONG。
- 掩码标志位“MASK”:表示帧内容是否使用异或操作(xor)做简单的加密。目前的 WebSocket 标准规定,客户端发送数据必须使用掩码,而服务器发送则必须不使用掩码。
- “Payload len”:表示帧内容的长度。它是另一种变长编码,最少 7 位,最多是 7+64 位,也就是额外增加 8 个字节,所以一个 WebSocket 帧最大是 2^64。
- “Masking-key”:掩码密钥,它是由上面的标志位“MASK”决定的,如果使用掩码就是 4 个字节的随机数,否则就不存在。
2 websocket握手过程
如下所示是笔者根据netty ws样例进行浏览器浏览的一个用例,可以看到这个websocket链接可以分为三个阶段:
- 建立tcp链接;
- 客户单采用http头进行ws握手;
- 协议由http切换成ws传输数据;
2.1 客户端的第一个GET
如下图所示是客户端websocket链接的第一个报文。
WebSocket 的握手是一个标准的 HTTP GET 请求,但要带上两个协议升级的专用头字段:
- “Connection: Upgrade”,表示要求协议“升级”;
- “Upgrade: websocket”,表示要“升级”成 WebSocket 协议。
另外,为了防止普通的 HTTP 消息被“意外”识别成 WebSocket,握手消息还增加了两个额外的认证用头字段(所谓的“挑战”,Challenge):
- Sec-WebSocket-Key:一个 Base64 编码的 16 字节随机数,作为简单的认证密钥;
- Sec-WebSocket-Version:协议的版本号,当前必须是 13。
2.2 服务端响应 SwitchingProtocol
服务器收到 HTTP 请求报文,看到上面的四个字段,就知道这不是一个普通的 GET 请求,而是 WebSocket 的升级请求,于是就不走普通的 HTTP 处理流程,而是构造一个特殊的“101 Switching Protocols”响应报文,通知客户端,接下来就不用 HTTP 了,全改用 WebSocket 协议通信。(有点像 TLS 的“Change Cipher Spec”)
WebSocket 的握手响应报文也是有特殊格式的,要用字段“Sec-WebSocket-Accept”验证客户端请求报文,同样也是为了防止误连接。
具体的做法是把请求头里“Sec-WebSocket-Key”的值,加上一个专用的 UUID “258EAFA5-E914-47DA-95CA-C5AB0DC85B11”,再计算 SHA-1 摘要。
客户端收到响应报文,就可以用同样的算法,比对值是否相等,如果相等,就说明返回的报文确实是刚才握手时连接的服务器,认证成功。
握手完成,后续传输的数据就不再是 HTTP 报文,而是 WebSocket 格式的二进制帧了。
2.3 客户端ws报文
客户端和服务端编码则更好验证了之前开头的总结:
WebSocket 的帧头就四个部分:“结束标志位 + 操作码 + 帧长度 + 掩码”;
2.4 服务端ws回复
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
机房租用,北京机房租用,IDC机房托管, http://www.fwqtg.net