引言
随着 Web
领域的飞速发展,最初设计得十分简单的 HTTP
协议在当今的应用上显得捉襟见肘,协议的瓶颈也逐渐显露出来。
“既然 HTTP
这么烂?为什么我们不抛弃它再设计一个全新的协议,而是要给他打补丁呢?”这是某次我和我舍友在宿舍喝着小酒,探讨 SpringMVC
时谈到的一个话题。
最初的 HTTP
,只是想把HTTP
当作传输 HTML
文档的协议,设计者没有想到 HTTP
经久不衰,需要承受越来越复杂的业务职责。
所以 HTTP
的设计无可厚非,只是它不“那么地”适应这个时代了。但是目前基于 HTTP
协议实现的 Web
浏览器遍布全球,并不能随意动摇 HTTP
的地位。
所以为了满足现代社会日益增长的业务需求,涌现出无数企图“消除”HTTP
瓶颈的解决方案,其中的一项就是——“WebSocket
”。
栗子
为什么会出现 WebSocket
协议呢?
假设我们去实现类似微信一样的网页在线聊天工具,我们要解决什么问题呢?
很显然,消息推送是当前业务场景下的一个技术瓶颈。
当前账号收到了消息,我们怎么在网页里“实时地”获取到消息并显示呢?注意要保障在线聊天的实时性。
方案一:只使用 HTTP
能实现吗?显然不能。
浏览器向客户端发起 HTTP
请求,请求到含有当前消息的 HTML
文件,渲染给用户。请求结束,不考虑持久连接的情况下,断开 TCP
连接。
当服务器有新消息时,除非用户当前网页刷新,否则在只使用 HTTP
的情况下无法实现实时性。
方案二:再加上 Ajax
Ajax
意思就是用JavaScript
执行异步网络请求。
——《廖雪峰的JavaScript
教程》
使用Ajax
,可以在浏览器内用JavaScript
“偷偷”地发起网络请求,查询最新的消息,降低操作成本,提升用户体验。
为了保证实时性,需要前台定时发送 Ajax
请求。例如:每 5s
发送一次网络请求查询消息,将数据的延时性保持在固定间隔以内。
缺点很明显,会产生大量的 HTTP
请求,如果消息长时间没有更新,十分浪费通信资源。
方案三:再加上 Comet
为了避免产生大量无用的 HTTP
请求,产生了Comet
。
Ajax
发起查询消息的请求,服务器接收到该消息,不会立即响应,Comet
会将响应置于挂起状态,当服务器端有内容更新时,再返回响应。
与方案二相比,减少了无用的 HTTP
请求,同时也保证了数据的实时性。
缺点就是服务器压力过大,服务器为了保持挂起的 HTTP
响应,会消耗更多的宝贵的服务器资源。
方案四:WebSocket
因为 HTTP
是请求响应模式,所以如果是基于 HTTP
实现实时性,过于复杂。请求都是客户端发起的,客户端怎么知道服务器的内容什么时候更新呢?
所以,2011 年 12 月 11 日
,RFC 6455 - The WebSocket Protocol
标准被确立。
WebSocket
,同样基于 TCP
协议,是 Web
浏览器与 Web
服务器之间的全双工通信标准。
如果有数据更新,服务器可以向客户端发送数据,使实现实时性变得简单。
协议详解
WebSocket
连接的建立需要握手,说是握手,其实也是为了兼容 HTTP
浏览器的一种手段。
握手之请求
客户端向服务器发送 HTTP
数据包,请求建立握手。
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: xxxxxxxxxx
Sec-WebSocket-Version: 13
具体的细节我就不描述了,自己去查阅文档。
客户端发送 HTTP
请求,首部 Upgrade: websocket
,表示客户端希望将通信协议从HTTP
升级为 WebSocket
协议。
握手之响应
服务器返回响应,同意切换协议。
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: xxxxxxxxxx
HTTP
响应状态码101
,切换协议。
通信
握手之后即建立起了连接,开始全双工通信。
WebSocket
与 HTTP
协议类似,其资源地址类似:ws://server.example.com:8080/path
。
总结
本文共同讨论了 WebSocket
协议的一些理论基础,夜深了,先写到这里。
下一篇文章,使用 spring-boot
实现简易的 WebSocket
示例。