作者:实验室2012级刘力超

Spring 4.0的一个最大更新是增加了websocket的支持。websocket提供了一个在web应用中的高效、双向的通讯,需要考虑到客户端(浏览器)和服务器之间的高频和低延时消息交换。一般的应用场景有:在线交易、网页聊天、游戏、协作、数据可视化等。

1、Web消息通信总结

以一个简单的网页聊天为应用场景,在Java EE环境下,其实现方式大抵有以下几种

 轮询:客户端定时向服务器发送Ajax请求,服务器接到请求后马上返回响应信息并关闭连接。 其主要用到js的setInterval()方法。
 优点:简单易懂,后端程序编写比较容易,不需要特别编写特别的代码。
 缺点:请求中有大半是无用,同时浪费带宽服务器资源。
 实例:适于小型应用。
 
 长轮询:客户端向服务器发送Ajax请求,服务器接到请求后hold住连接,直到有新消息才返回响应信息并关闭连接,客户端处理完响应信息后再向服务器发送新的请求。
 优点:在无消息的情况下不会频繁的请求,耗费资源小。
 缺点:服务器hold连接会消耗资源,返回数据顺序无保证,难于管理维护。
 实例:WebQQ、Hi网页版、Facebook IM。
 
 长连接:在页面里嵌入一个隐蔵iframe,将这个隐蔵iframe的src属性设为对一个长连接的请求或是采用xhr请求,服务器端就能源源不断地往客户端输入数据。
 优点:消息即时到达,不发无用请求;管理起来也相对方便。
 缺点:服务器维护一个长连接会增加开销。
 实例:Gmail聊天
 
 Flash Socket:在页面中内嵌入一个使用了Socket类的 Flash 程序JavaScript通过调用此Flash程序提供的Socket接口与服务器端的Socket接口进行通信,JavaScript在收到服务器端传送的信息后控制页面的显示。 
 优点:实现真正的即时通信,而不是伪即时。 
 缺点:客户端必须安装Flash插件;非HTTP协议,无法自动穿越防火墙。 
 实例:网络互动游戏。

传统 Web 模式在处理高并发及实时性需求的时候,会遇到难以逾越的瓶颈,随着连接数的增多,服务器资源将很快被耗尽,因此一种新的规范随之诞生,websocket是在物理层非网络层建立一条客户端至服务器的长连接,以此来保证服务器向客户端的即时推送,既不耗费线程资源,又不会不断向服务器轮询请求。

2、WebSocket机制

WebSocket 是 HTML5 一种新的协议。它实现了浏览器与服务器全双工通信,能更好的节省服务器资源和带宽并达到实时通讯,它建立在 TCP 之上,同 HTTP 一样通过 TCP 来传输数据,但是它和 HTTP 最大不同是:

  • WebSocket 是一种双向通信协议,在建立连接后,WebSocket 服务器和 Browser/Client Agent 都能主动的向对方发送或接收数据,就像 Socket 一样;
  • WebSocket 需要类似 TCP 的客户端和服务器端通过握手连接,连接成功后才能相互通信。

非 WebSocket 模式传统 HTTP 客户端与服务器的交互如下图所示:

【技术教程】Java Spring WebSocket实现后端消息主动推送(上)-数字人文技术实验室

传统HTTP客户端请求响应模式

而与传统HTTP通信相比,WebSocket的模式如下:

【技术教程】Java Spring WebSocket实现后端消息主动推送(上)-数字人文技术实验室

从上可以看出,在连接生效过后,Server端可以实现向Browser端主动的消息推送。一旦 WebSocket 连接建立后,后续数据都以帧序列的形式传输。在客户端断开 WebSocket 连接或 Server 端断掉连接前,不需要客户端和服务端重新发起连接请求。在海量并发及客户端与服务器交互负载流量大的情况下,极大的节省了网络带宽资源的消耗,有明显的性能优势,且客户端发送和接受消息是在同一个持久连接上发起,实时性优势明显。

而HTTP与WebSocket的通信交互过程中的报文也有所区别:

WebSocket Request部分报文:

Host:localhost:8080
Origin:http://localhost:8080
Sec-WebSocket-Extensions:permessage-deflate; client_max_window_bits
Sec-WebSocket-Key:39rBeP3SAnLxTYggKvOcSg==
Sec-WebSocket-Version:13
Upgrade:websocket

可以看到,客户端发起的 WebSocket 连接报文类似传统 HTTP 报文,”Upgrade:websocket”参数值表明这是 WebSocket 类型请求,“Sec-WebSocket-Key”是 WebSocket 客户端发送的一个 base64 编码的密文,要求服务端必须返回一个对应加密的“Sec-WebSocket-Accept”应答,否则客户端会抛出“Error during WebSocket handshake”错误,并关闭连接。

WebSocket Response部分报文:

Connection:upgrade
Date:Fri, 06 May 2016 07:00:18 GMT
Sec-WebSocket-Accept:U9tI2JJDzgtcvS55TRoT816bBYA=
Sec-WebSocket-Extensions:permessage-deflate;client_max_window_bits=15
Server:Apache-Coyote/1.1
Upgrade:websocket

“Sec-WebSocket-Accept”的值是服务端采用与客户端一致的密钥计算出来后返回客户端的,“HTTP/1.1 101 Switching Protocols”表示服务端接受 WebSocket 协议的客户端连接,经过这样的请求-响应处理后,客户端服务端的 WebSocket 连接握手成功, 后续就可以进行 TCP 通讯了。读者可以查阅WebSocket 协议栈了解 WebSocket 客户端和服务端更详细的交互数据格式。