使用WebSocket构建实时WEB
为了防止无良网站的爬虫抓取文章,特此标识,转载请注明文章出处。LaplaceDemon/SJQ。
http://www.cnblogs.com/shijiaqi1066/p/3795075.html
1 WebSocket与传统Web实时通信技术
1.1 WebSocket
HTTP是一种典型的单工模式。即基于Request/Response的方式与服务器进行交互。HTML5提供了浏览器与服务端的双工通信协议WebSocket。
1.2传统Web实时通信技术
- 轮询
- Comet
- 长轮询
- Flash XML Socket
轮询,对服务器压力较大,实时性差,效率低下。
移动端对Flash支持的非常差。Flash经常全局性的崩溃,很不稳定。
Comet与长轮询应该是传统方案中最好的Web实时交互的技术。这两种技术本质是一种Hack技术。Comet与长轮询相比有许多缺点。但这不影响它们在传统实时Web中的应用。这两项技术最大的问题在于每次交互的HTTP包的header内容过多,真正有实际意义的信息可能很少,从而导致网络利用率非常低。
对于长轮询技术,由于离不开请求/响应的模式,所以服务器压力也会较大。
2 WebSocket协议
WebSocket与HTTP协议都是基于TCP的,都是可靠的协议。WebSocket和Http协议一样都属于应用层的协议。WebSocket使用标准的80和443端口,这两个端口都是防火墙的友好端口所以不需要防火墙的允许。
WebSocket协议的格式为 "ws://IP:Port" 或者"wss://IP:Port"。其中wss表示进行加密传输的WebSocket协议。
WebSocket协议需要进行"握手"。该"握手"阶段是通过HTTP协议进行的,"握手"行为通过Request/Response的Header完成,只需要交换很少的数据,便可以创建基于TCP/IP协议的双工通道。
该图引用自http://blog.sina.com.cn/s/blog_acddf95d0101beuj.html
浏览器与服务器通过TCP三次握手建立连接,如果连接建立失败,则浏览器将收到错误消息通知。
TCP建立连接成功后,浏览器通过HTTP协议传送WebSocket支持的版本号,协议的字版本号,原始地址,主机地址等等一些列字段给服务器端。
2.1 WebSocket 握手
WebSocket协议握手协议非常简单。
实例:运行Tomcat7提供的WebSocket Echo示例程序。当发生Connect时,Chrome中拦截到信息与截包工具中所得的报文如下所示。
Chrome所示
截包工具所示
请求报文
说明:
Connection:Upgrade与Upgrade:WebSocket 表示本次请求是要进行WebSocket的握手动作。
Sec-WebSocket-Version首部的值,表示浏览器支持的WebSocket版本信息。
Sec-WebSocket-Key首部的值,是一个由客户端随机生成的字符串。
响应报文
说明:
在服务器响应的握手信息中Sec-WebSocket-Accept的值为服务器通过客户端Header的Sec-WebSocket-Key的值进行计算并加密的结果。
服务器的响应状态为101,表示服务器端已经理解了客户端的需求,并且客户端需要根据Upgrade中的协议类型,切换为新的协议来完成后续的通信。
经过以上报文的通信候,基于WebSocket的TCP/IP双工通道就已经建立了。
2.2 握手验证
最老的WebSocket草案标准中是没有安全key。草案7.5、7.6中有两个安全key。草案10中只有一个安全key。
7.5、7.6中HTTP头中的"Sec-WebSocket-Key1"与"Sec-WebSocket-Key2"在10中合并为了一个"Sec-WebSocket-Key"。HTTP头中Upgrade的值由"WebSocket"修改为了"websocket"。HTTP头中的"-Origin"修改为了"Sec-WebSocket-Origin"。
增加了HTTP头"Sec-WebSocket-Accept",用来返回原来草案7.5、7.6服务器返回给客户端的握手验证,原来是以内容的形式返回,现在是放到了HTTP头中。
服务器生成验证的方式变化较大。
旧版WebSocket
旧版生成Token的方法如下:
取出Sec-WebSocket-Key1中的所有数字字符形成一个数值,这里是1427964708,然后除以Key1中的空格数目,得到一个数值,保留该数值整数位,得到数值N1;对Sec-WebSocket-Key2采取同样的算法,得到第二个整数N2;把N1和N2按照Big-Endian字符序列连接起来,然后再与另外一个Key3连接,得到一个原始序列ser_key。Key3是指在握手请求最后,有一个8字节的奇怪的字符串“;”######”,这个就是Key3。然后对ser_key进行一次md5运算得出一个16字节长的digest,这就是老版本协议需要的token,然后将这个token附在握手消息的最后发送回Client,即可完成握手。
新版WebSocket
新版生成Token的方法如下:
首先服务器将key(长度24)截取出来,如4tAjitqO9So2Wu8lkrsq3w==,用它和自定义的一个字符串(长度36)258EAFA5-E914-47DA-95CA-C5AB0DC85B11连接起来,然后把这一字符串进行SHA-1算法加密,得到长度为20字节的二进制数据,再将这些数据经过Base64编码,最终得到服务端的密钥,也就是ser_key。服务器将ser_key附在返回值Sec-WebSocket-Accept后,至此握手成功。
2.3 数据报文格式
旧版协议比较简单,仅仅是在原始数据前加了个’\x00′,在最后面加了个’\xFF’,即假如Client发送一个字符串’test’,实际上WebSocket Server收到的数据是:’x00test\xFF’,所以只需要剥离掉首尾那两个字符就可以了。
新版的协议对这部分规定比较复杂,以下是其格式标准:(下图在Firefox可能会出现错乱,请换用Chrome)
FIN:1位,用来表明这是一个消息的最后的消息片断,当然第一个消息片断也可能是最后的一个消息片断;
RSV1, RSV2, RSV3:分别都是1位,如果双方之间没有约定自定义协议,那么这几位的值都必须为0,否则必须断掉WebSocket连接;
Opcode:4位操作码,定义有效负载数据,如果收到了一个未知的操作码,连接也必须断掉,以下是定义的操作码:
%x0 表示连续消息片断
%x1 表示文本消息片断
%x2 表未二进制消息片断
%x3-7 为将来的非控制消息片断保留的操作码
%x8 表示连接关闭
%x9 表示心跳检查的ping
%xA 表示心跳检查的pong
%xB-F 为将来的控制消息片断的保留操作码
Mask:1位,定义传输的数据是否有加掩码,如果设置为1,掩码键必须放在masking-key区域,客户端发送给服务端的所有消息,此位的值都是1;
Payload length:传输数据的长度,以字节的形式表示:7位、7+16位、或者7+64位。如果这个值以字节表示是0-125这个范围,那这个值就表示传输数据的长度;如果这个值是126,则随后的两个字节表示的是一个16进制无符号数,用来表示传输数据的长度;如果这个值是127,则随后的是8个字节表示的一个64位无符合数,这个数用来表示传输数据的长度。多字节长度的数量是以网络字节的顺序表示。负载数据的长度为扩展数据及应用数据之和,扩展数据的长度可能为0,因而此时负载数据的长度就为应用数据的长度。
Masking-key:0或4个字节,客户端发送给服务端的数据,都是通过内嵌的一个32位值作为掩码的;掩码键只有在掩码位设置为1的时候存在。 Payload data: (x+y)位,负载数据为扩展数据及应用数据长度之和。 Extension data:x位,如果客户端与服务端之间没有特殊约定,那么扩展数据的长度始终为0,任何的扩展都必须指定扩展数据的长度,或者长度的计算方式,以及在握手时如何确定正确的握手方式。如果存在扩展数据,则扩展数据就会包括在负载数据的长度之内。 Application data:y位,任意的应用数据,放在扩展数据之后,应用数据的长度=负载数据的长度-扩展数据的长度。
2.4 WebSocket版本与浏览器支持
表格来自于:http://zh.wikipedia.org/wiki/WebSocket
3 Web前端相关的WebSocket
3.1 W3C API定义
Websocket的API非法简单,以下是W3C的定义
enum BinaryType { "blob", "arraybuffer" };
[Constructor(DOMString url, optional (DOMString or DOMString[]) protocols)]
interface WebSocket : EventTarget {
readonly attribute DOMString url;
// ready state
const unsigned short CONNECTING = 0;
const unsigned short OPEN = 1;
const unsigned short CLOSING = 2;
const unsigned short CLOSED = 3;
readonly attribute unsigned short readyState;
readonly attribute unsigned long bufferedAmount;
// networking
attribute EventHandler onopen;
attribute EventHandler onerror;
attribute EventHandler onclose;
readonly attribute DOMString extensions;
readonly attribute DOMString protocol;
void close([Clamp] optional unsigned shortcode, optional DOMString reason);
// messaging
attribute EventHandler onmessage;
attribute BinaryType binaryType;
void send(DOMString data);
void send(Blob data);
void send(ArrayBuffer data);
voidsend(ArrayBufferView data);
};
3.2 实际使用的简单例子
例1
var wsServer = 'ws://localhost:8888/Demo';
var websocket = new WebSocket(wsServer); websocket.onopen = function (evt) { onOpen(evt) };
websocket.onclose = function (evt) { onClose(evt) };
websocket.onmessage = function (evt) { onMessage(evt) };
websocket.onerror = function (evt) { onError(evt) }; function onOpen(evt) {
console.log("Connected to WebSocket server.");
} function onClose(evt) {
console.log("Disconnected");
} function onMessage(evt) {
console.log('Retrieved data from server: ' + evt.data);
} function onError(evt) {
console.log('Error occured: ' + evt.data);
}
例2
<!DOCTYPE html>
<meta charset="utf-8" />
<title>WebSocket Test</title>
<script language="javascript"type="text/javascript"> var wsUri ="ws://echo.websocket.org/";
var output;
function init() {
output = document.getElementById("output");
testWebSocket();
} function testWebSocket() {
websocket = new WebSocket(wsUri);
websocket.onopen = function(evt) {
onOpen(evt)
};
websocket.onclose = function(evt) {
onClose(evt)
}; websocket.onmessage = function(evt) {
onMessage(evt)
}; websocket.onerror = function(evt) {
onError(evt)
};
} function onOpen(evt) {
writeToScreen("CONNECTED");
doSend("WebSocket rocks");
} function onClose(evt) {
writeToScreen("DISCONNECTED");
} function onMessage(evt) {
writeToScreen('<span style="color: blue;">RESPONSE: '+ evt.data+'</span>');
websocket.close();
} function onError(evt) {
writeToScreen('<span style="color: red;">ERROR:</span> '+ evt.data);
} function doSend(message) {
writeToScreen("SENT: " + message);
websocket.send(message);
} function writeToScreen(message) {
var pre = document.createElement("p");
pre.style.wordWrap = "break-word";
pre.innerHTML = message;
output.appendChild(pre);
} window.addEventListener("load", init, false); </script>
<h2>WebSocket Test</h2>
<div id="output"></div>
</html>
代码简述
使用new创建WebSocket对象。
var wsUri ="ws://echo.websocket.org/";
websocket = new WebSocket(wsUri);
WebSocket对象一共支持四个事件响应方法 onopen, onmessage, onclose和onerror。这样不会阻塞UI,得到更好的用户体验。
当浏览器和服务器连接成功后,会触发onopen事件。
websocket.onopen = function(evt) {};
如果连接失败,发送、接收数据失败或者处理数据出现错误,会触发onerror事件。
websocket.onerror = function(evt) {};
当浏览器接收到服务器发送过来的数据时,就会触发onmessage事件。参数evt中包含服务器传输过来的数据;
websocket.onmessage = function(evt) {};
当浏览器接收到服务器发送的关闭连接请求时,就会触发onclose事件。
websocket.onclose = function(evt) {};
在实际应用中还需要考虑心跳包的问题。
4 WebSocket的缺点
最大的问题就是浏览器兼容性问题。低版本IE浏览器不支持该技术,直到IE10才开始支持WebSocket技术。
当然,解决方案是对于低版本浏览器可以使用Flash来模拟WebSocket。
如:web-socket-js 地址:https://github.com/gimite/web-socket-js/
5 WebSocket的服务器实现
WebSocket在网上有非常多实现的例子。以下列出个人会学习研究的服务器实现。
- Netty实现
- Tomcat7实现
- JavaEE7+Tomcat8实现
- Node.js实现
参考资料
http://zh.wikipedia.org/wiki/WebSocket
http://www.web-tinker.com/search/websocket/1.html
http://www.ibm.com/developerworks/cn/web/1112_huangxa_websocket/
http://hackecho.com/2012/04/new-hybi-10-protocol-of-websocket/
http://www.zendstudio.net/archives/web-socket-heartbeat-package/
为了防止无良网站的爬虫抓取文章,特此标识,转载请注明文章出处。LaplaceDemon/SJQ。
http://www.cnblogs.com/shijiaqi1066/p/3795075.html
使用WebSocket构建实时WEB的更多相关文章
- [转]使用 HTML5 WebSocket 构建实时 Web 应用
HTML5 WebSocket 简介和实战演练 本文主要介绍了 HTML5 WebSocket 的原理以及它给实时 Web 开发带来的革命性的创新,并通过一个 WebSocket 服务器和客户端的案例 ...
- 使用 HTML5 WebSocket 构建实时 Web 应用
原文地址:http://www.ibm.com/developerworks/cn/web/1112_huangxa_websocket/ HTML5 WebSocket 简介和实战演练 本文主要介绍 ...
- (转)使用 HTML5 WebSocket 构建实时 Web 应用
HTML5 WebSocket 简介和实战演练 本文主要介绍了 HTML5 WebSocket 的原理以及它给实时 Web 开发带来的革命性的创新,并通过一个 WebSocket 服务器和客户端的案例 ...
- SpringBoot学习笔记(11):使用WebSocket构建交互式Web应用程序
SpringBoot学习笔记(11):使用WebSocket构建交互式Web应用程序 快速开始 本指南将引导您完成创建“hello world”应用程序的过程,该应用程序在浏览器和服务器之间来回发送消 ...
- 使用SuperWebSocket 构建实时 Web 应用
Web 应用的信息交互过程通常是客户端通过浏览器发出一个请求,服务器端接收和审核完请求后进行处理并返回结果给客户端,然后客户端浏览器将信息呈现出来,这种机制对于信息变化不是特别频繁的应用尚能相安无事, ...
- Socket.IO – 基于 WebSocket 构建跨浏览器的实时应用
Socket.IO 是一个功能非常强大的框架,能够帮助你构建基于 WebSocket 的跨浏览器的实时应用.支持主流浏览器,多种平台,多种传输模式,还可以集合 Exppress 框架构建各种功能复杂 ...
- 基于 WebSocket 构建跨浏览器的实时应用
Socket.IO – 基于 WebSocket 构建跨浏览器的实时应用 Socket.IO 是一个功能非常强大的框架,能够帮助你构建基于 WebSocket 的跨浏览器的实时应用.支持主流浏览器,多 ...
- 实时Web与WebSocket实践
引言:实时Web越来越被重视,Google.Facebook等大公司也逐渐开始提供实时性服务.实时Web将是未来最热门的话题之一. 本文选自<基于MVC的JavaScript Web富应用开发 ...
- 【读书笔记】2016.12.10 《构建高性能Web站点》
本文地址 分享提纲: 1. 概述 2. 知识点 3. 待整理点 4. 参考文档 1. 概述 1.1)[该书信息] <构建高性能Web站点>: -- 百度百科 -- 本书目录: 第1章 绪论 ...
随机推荐
- imacros实现Excel数据自动录入到网页中
一.工具选择 最近接到一个项目,需要将excel数据逐条录入.保存到网页中.经过搜集资料,能实现功能的大概有以下几种方式,按键精灵.autoit.imacros.python+selenium. 按键 ...
- Welcome to JimmyCheung's blog!
博客开通,写写学习笔记,写写心情,写写生活中的点点滴滴~ 有钱的捧个钱场嘞,没钱的贡献个点击量,新鲜的博客出炉咯,来五毛钱的博文呗~ By Jimmy 2014.09.16
- html5自定义标签选择器
* E[attr]:只使用属性名,但没有确定任何属性值 * E[attr="value"]:指定属性名,并指定了该属性的属性值.必须和元素的属性完全匹配 * E[attr~=&qu ...
- httpclient 超时设置
最近项目客户反应超时经常出现:现已经总结超时设置: 使用是apache的HttpClient: DefaultHttpClient:请求超时httpclient.getParams().setPara ...
- java基础随笔-内部类
今天来复习下内部类的一些基础知识. 首先是内部类的分类: 1.成员内部类 2.静态内部类 3.匿名内部类 4.局部内部类 下面逐一来介绍下. 首先是成员内部类,就是将内部类作为一个成员变量来处理.具体 ...
- Service Locator 模式
什么是Service Locator 模式? 服务定位模式(Service Locator Pattern)是一种软件开发中的设计模式,通过应用强大的抽象层,可对涉及尝试获取一个服务的过程进行封装.该 ...
- 【转】MFC界面更新实现方法
原文网址:http://www.cnblogs.com/skywatcher/p/3572311.html 1.更新窗口 即采用UpdateWindow()函数立即发送WM_PAINT消息更新整个窗口 ...
- Delphi中WideString类型如何转化成String类型
var wstr:WideString; str:string; begin str:=WideCharToString(PWideChar(wstr)); end;
- 用Delphi实现WinSocket高级应用
用Delphi实现WinSocket高级应用 默认分类 2009-12-19 16:48 阅读6 评论0 字号: 大大 中中 小小 Socket通信在Windows 中是排队的形式 ...
- unity3d shader之Roberts,Sobel,Canny 三种边缘检测方法
方法其实都差不多,就是用两个过滤器,分别处理两个分量 Sobel算子 先说Sobel算子 GX为水平过滤器,GY为垂直过滤器,垂直过滤器就是水平过滤器旋转90度.过滤器为3x3的矩阵,将与图像作平面卷 ...