WebSocket科普
WebSocket为何物?如果你现在还不太清楚请先到baidu百科一下。如果你实在懒得起搜索的话,下面帮你从网络上搜罗了些许有关WebSocket的信息。
Baidu百科:http://baike.baidu.com/view/3623887.htm (下面摘录了些许内容,内容来自Baidu)
WebSocket 规范的目标是在浏览器中实现和服务器端双向通信.双向通信可以拓展浏览器上的应用类型,例如实时的数据推送(股票行情),游戏,聊天/im 等.
背景:目前在浏览器中通过http仅能实现单向的通信,comet可以一定程度上模拟双向通信,但效率较低,并需要服务器有较好的支持; flash中的socket和xmlsocket可以实现真正的双向通信,通过 flex ajax bridge,可以在javascript中使用这两项功能. 可以预见,如果websocket一旦在浏览器中得到实现,将会替代上面两项技术,得到广泛的使用.面对这种状况,HTML5定义了WebSocket协议,能更好的节省服务器资源和带宽并达到实时通讯。
原理:websocket规范由两部分组成,一部分是浏览器中的 websocket api, 由w3c 制订, 一部分是websocket 协议, 由ietf制订,目前是draft状态.websocket的协议比较简单, 客户端和普通的浏览器一样通过80或者443端口和服务器进行请求握手,服务器根据http header识别是否一个websocket请求,如果是,则将请求升级为一个websocket连接,握手成功后就进入双向长连接的数据传输阶段. websocket的数据传输是基于帧的方式: 0x00 表示数据开始, 0xff表示数据结束,数据以utf-8编码.
握手协议:
在实现websocket连线过程中,需要透过浏览器发出websocket连线请求,然后服务器发出回应,这个过程通常称为“握手” (handshaking)。
PS1:握手协议在后期的版本中,会标明版本编号,下面的例子属于早期的协定之一,对于新版的 chrome 和 Firefox 皆不适用。
PS2:后期的版本大多属于功能上的扩充,例如使用第7版的握手协议同样也适用于第8版的握手协议。
例子:
浏览器请求
GET /demo HTTP/1.1
Host: 你的网址.com
Connection: Upgrade
Sec-WebSocket-Key2: 12998 5 Y3 1 .P00
Sec-WebSocket-Protocol: sample
Upgrade: WebSocket
Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5
Origin: http://你的网址.com
^n:ds[4U
服务器回应
HTTP/1.1 101
WebSocket Protocol Handshake
Upgrade: WebSocket
Connection: Upgrade
Sec-WebSocket-Origin: http://你的网址.com
Sec-WebSocket-Location: ws://你的网址.com/demo
Sec-WebSocket-Protocol: sample
8jKS’y:G*Co,Wxa-
支持的浏览器
目前实现了websocket的浏览器:
Chrome Supported in version 4+
Firefox Supported in version 4+
Internet Explorer Supported in version 10+
Opera Supported in version 10+
Safari Supported in version 5+
支持的服务器
在服务器端,也出现了一些实现websocket协议的项目:
jetty 7.0.1 包含了一个初步的实现
resin 包含有websocket 实现
pywebsocket, apache http server 扩展
apache tomcat 7.0.27 版本
websocket api在浏览器端的广泛实现似乎只是一个时间问题了, 值得注意的是目前服务器端没有标准的api, 各个实现都有自己的一套api, 并且jcp也没有类似的提案, 所以使用websocket开发服务器端有一定的风险.可能会被锁定在某个平台上或者将来*升级.
Baidu百科对Websocket做了基本的介绍。WebSocket是html5的一部分,而html5标准还处于制定过程中。根据网上的信息得知到2014年底,HTML5才将成为一种完整的成品标准(来自 http://www.iteye.com/news/26113),W3C计划到2016年底发布后续版本HTML 5.1。而且 新的HTML 5.1将包含较少的技术,曾经包含在HTML 5之下的Web Workers 和WebSockets现在都将成为单独标准。可见websocket还处于一个快速发展变化的过程中。
从WebSocket的协议的握手处理和数据传输格式的快速变化可窥其一斑。WebSocket协议握手处理和消息格式的变迁(来自网络:http://lchshu001.iteye.com/blog/1184428)
1.握手协议
版本0--3中:
握手通过请求头Sec-WebSocket-Key1 和 Sec-WebSocket-Key2 的值和 8 字节的请求实体,进行MD5加密,将加密结果,构造出一个16字节作为请求实体的内容返回。如下实例:
------------------请求--------------------------------------------
- GET /demo HTTP/1.1
- Host: example.com
- Connection: Upgrade
- Sec-WebSocket-Key2: 12998 5 Y3 1 .P00
- Sec-WebSocket-Protocol: sample
- Upgrade: WebSocket
- Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5
- Origin: http://example.com
- (\r\n)
- ^n:ds[4U
------------------响应--------------------------------------------
- HTTP/1.1 101 WebSocket Protocol Handshake
- Upgrade: WebSocket
- Connection: Upgrade
- Sec-WebSocket-Origin: http://example.com
- Sec-WebSocket-Location: ws://example.com/demo
- Sec-WebSocket-Protocol: sample
- (\r\n)
- 8jKS'y:G*Co,Wxa-
------------------------------------------------------------------
把第一个Key中的数字除以第一个Key的空白字符的数量,而第二个Key也是如此,这样得到两个整数,把每个整数写的四个字节里去,串为8个字节,然后和请求实体里面的8个字节串为16字节,将这16个字节进行MD5加密(如实例中的结果:8jKS'y:G*Co,Wxa-),得到一个16字节的数据作为响应实体的内容,返回给客户端,这样握手成功。
在版本4之后,握手协议修改了:
------------------请求--------------------------------------------
- GET /chat HTTP/1.1
- Host: server.example.com
- Upgrade: websocket
- Connection: Upgrade
- Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
- Sec-WebSocket-Origin: http://example.com
- Sec-WebSocket-Protocol: chat, superchat
- (\r\n)
------------------响应--------------------------------------------
- HTTP/1.1 101 Switching Protocols
- Upgrade: websocket
- Connection: Upgrade
- Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo=
- Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC==
- Sec-WebSocket-Protocol: chat
使用请求头的值 Sec-WebSocket-Key,该值是BASE-64编码(base64-encoded)的,我们不需要转码,加上一个魔幻字符串: "258EAFA5-E914-47DA-95CA-C5AB0DC85B11",(结果:[dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11])使用 SHA-1 加密,之后进行 BASE-64编码,将结果做为 Sec-WebSocket-Accept 头的值,返回给客户端。
如果服务器端有 Sec-WebSocket-Nonce 头,表示要在Sec-WebSocket-Key 的值,和魔幻字符串之间加入该 Sec-WebSocket-Nonce 头的值,即“dGhlIHNhbXBsZSBub25jZQ==AQIDBAUGBwgJCgsMDQ4PEC==258EAFA5-E914-47DA-95CA-C5AB0DC85B11”,进行 SHA-1 加密,之后和前面的相同。完成握手协议。
2.消息帧格式
在版本 0 中, 数据帧比较的简单。数据帧以 0x00 开头,以0xFF结尾,中间的数据以utf-8编码的字符就可以了。当然这个简单的格式只能用来传输字符串。无法传输字节流。所以 版本 1 就做了修改了,后面的版本绝大部分是兼容的。
后面的这个帧结构就有点复杂了,如下所示(一行是4个字节,32 bit):
- 0 1 2 3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- +-+-+-+-+-------+-+-------------+-------------------------------+
- |M|R|R|R| opcode|R| Payload len | Extended payload length |
- |O|S|S|S| (4) |S| (7) | (16/63) |
- |R|V|V|V| |V| | (if payload len==126/127) |
- |E|1|2|3| |4| | |
- +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
- | Extended payload length continued, if payload len == 127 |
- + - - - - - - - - - - - - - - - +-------------------------------+
- | | Extension data |
- +-------------------------------+ - - - - - - - - - - - - - - - +
- : :
- +---------------------------------------------------------------+
- : Application data :
- +---------------------------------------------------------------+
[MORE] 表示一个数据通过多个帧进行传输, 如果是 0 表示后面还有数据帧,如果是 1 则表示是最后一个帧。
[RSV1][RSV2][RSV3][RSV4] 未做定义暂时全为零。
[opcode] 标识数据的格式,以及帧的控制,如:08标识数据内容是 文本,01标识:要求远端去关闭当前连接。
[Payload len] 如果小于126 表示后面的数据长度是 [Payload len] 的值。(最大125byte)
等于 126 表示之后的16 bit位的数据值标识数据的长度。(最大65535byte)
等于 127 表示之后的64 bit位的数据值标识数据的长度。(一个有符号长整型的最大值)
[Extension data]没有提及怎么使用。
[Application data] 为应用提供的数据。
版本7之后,添加了 MASK 的概念。相当于对数据加密。而且要求客户端必须是MASK的。
- 0 1 2 3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- +-+-+-+-+-------+-+-------------+-------------------------------+
- |F|R|R|R| opcode|M| Payload len | Extended payload length |
- |I|S|S|S| (4) |A| (7) | (16/63) |
- |N|V|V|V| |S| | (if payload len==126/127) |
- | |1|2|3| |K| | |
- +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
- | Extended payload length continued, if payload len == 127 |
- + - - - - - - - - - - - - - - - +-------------------------------+
- | |Masking-key, if MASK set to 1 |
- +-------------------------------+-------------------------------+
- | Masking-key (continued) | Payload Data |
- +-------------------------------- - - - - - - - - - - - - - - - +
- : Payload Data continued ... :
- + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
- | Payload Data continued ... |
- +---------------------------------------------------------------+
[opcode] 01标识数据内容是 文本,08标识 : 要求远端去关闭当前连接。
[MASK](即原先的RSV4)如果是 1 则数据是被 MASK 的。
[Masking-key] 如果MASK为 1 则有4字节的 Masking-key,用于与传输的数据 [Payload Data] 进行异或运算,4byte(32bit)进行一次运算,不足四位从前往后对应,如只有三位,则只与[Masking-key]的前三位进行运算。
3.关闭远端连接:
在版本 0 时:传两个字节 (0xff,0x00);
在版本 1--6 时:传三个字节 (0x80,0x01,0x00);
在版本 7--以上 时:传两个字节 (0x88,0x00);
经测试 只有 在版本 7--以上 时:传两个字节 (0x88,0x00)。
以上 WebSocket握手协议处理,消息格式和关闭远端的变迁摘录自网络。
Websocket协议草案:http://tools.ietf.org/html/rfc6455 ;如果你对自己的E文有足够的信心,有足够的时间你可以去阅读翻译WebSocket协议。协议草案版本更新的很频繁。
尽管Websocket协议还处于一个快速进化发展的过程中,但我们不能等到他完全成熟之后再去学习使用他。因为移动互联网时代的道来,我们真的很需要他。
Html5支持的WebSocket AIP:(一下内容摘自:http://edu.cnzz.cn/Webmaster/Site/2/2012/0807/31898.html)
WebSocket 的js API是非常的简单:
见上图:ready state中定义的是socket的状态,分为connection、open、closing和closed四种状态,从字面上就可以区分出它们所代表的状态。
上图描述的是WebSocket的事件,分为onopen、onerror和onclose;
上图为消息的定义,主要是接收和发送消息。注意:可以发送二进制的数据。
以上个图的具体的含义就不再一一赘述,详细描述请参考:http://www.w3.org/TR/2012/WD-websockets-20120524/
PS:由于WebSocket API(截止到2012年7月)还是草案,API文档和上文所描述的会有所不同,请以官方文档为主,这也是我为什么不详细描述API中各个属性的原因。
另外一点需要提醒大家的是:在前端开发中,浏览器兼容是必不可少的,而WebSocket在主浏览器中的兼容还是不错的,火狐和Chrome不用说,最新版的支持非常不错,而且支持二进制数据的发送和接收。
下面给出一个html中的实例:(一下内容摘自:http://www.sztaixie.com/blackie/?p=123)
下面的代码片段是打开一个连接,为连接创建事件监听器,断开连接,消息时间,发送消息返回到服务器,关闭连接。
var socket = new WebSocket(‘ws://localhost:8080′);
// 打开Socket
socket.onopen = function(event) {
// 发送一个初始化消息
socket.send(‘I am the client and I\’m listening!’);
// 监听消息
socket.onmessage = function(event) {
console.log(‘Client received a message’,event);
};
// 监听Socket的关闭
socket.onclose = function(event) {
console.log(‘Client notified socket has closed’,event);
};
// 关闭Socket….
//socket.close()
};
让我们来看看上面的初始化片段。参数为URL,ws表示WebSocket协议。onopen、onclose和onmessage方法把事件连接到Socket实例上。每个方法都提供了一个事件,以表示Socket的状态。
onmessage事件提供了一个data属性,它可以包含消息的Body部分。消息的Body部分必须是一个字符串,可以进行序列化/反序列化操作,以便传递更多的数据。
WebSocket的语法非常简单,使用WebSockets是难以置信的容易……除非客户端不支持WebSocket。