WebSockets浅探及实践 - 1
1 起源
在WebSocket没有出现之前,如果我们要实现从服务器向客户端推送数据的功能,就必须采取某些work around,诸如Http polling(Comet),BOSH(XMPP)等。 但是他们都基于HTTP协议之上,频繁建立连接的开销以及冗余的HTTP头信息,都带来了一定的延迟。由此,WebSockets也就应运而生了。
2 规范规范细节请详阅这里
连接建立过程当打开了到服务器的连接后,客户端必须向服务器发送一次opening握手--它由HTTP Upgrade请求以及一系列必须和可选的头部数据组成。
- 请求必须是Get方法,且HTTP协议版本至少为1.1。
- 必须有Host,Upgrade(值必须包含websocket关键字),Connection(值必须包含"Upgrade"),Sec-WebSocket-Key请求头。
- 如果请求者是浏览器,必须包含Origin头。
- 必须包含Sec-WebSocket-Version头,且其值为13。
- 可以包含Sec-WebSocket-Protocol以及Sec-WebSocket-Extensions头
- 请求可以包含任何其他的头部域,譬如cookies或认证相关的头如Authorization。
而后客户端就在等待服务器端的响应并进行验证:
- 确定状态码是不是101,如果不是则遵循HTTP的响应处理过程。否则往下走
- 确保响应头中必须包含Upgrade域,其中包含"websocket"值,否则连接失败;
- 确保响应头包含Connect域,且有值"Upgrade",否则连接失败;
- 确保响应头包含Sec-WebSocket-Accept域,且其值应该等于Base64(SHA-1(|Sec-WebSocket-Key|)+"258EAFA5-E914-47DA-95CA-C5AB0DC85B11");否则连接失败;
- 如果响应头中包含了Sec-WebSocket-Extensions域,而请求头并未包含,那么客户端必须中断连接;
- 如果响应头中包含Sec-WebSocket-Protocol域,但在请求头中并没有包含此子协议值,客户端必须中断连接.
典型的请求头结构如下:
GET /socket.io/1/websocket/1166156958668058955 HTTP/1.1 Host: localhost:8077 User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:12.0) Gecko/20100101 Firefox/12.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-us,en;q=0.5 Accept-Encoding: gzip, deflate Connection: keep-alive, Upgrade Sec-WebSocket-Version: 13 Origin: http://localhost:8077 Sec-WebSocket-Key: xQelQMxXk7J8q1whTHKPJA== Pragma: no-cache Cache-Control: no-cache Upgrade: websocket响应头结构如下:
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: V6OpbVjbdllb6RNrRdFmG+pIzXk=
数据传输格式
在Spec中,数据传输格式的定义如下:
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/64) | |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 ... | +---------------------------------------------------------------
Close状态码
Status Code | Detail |
1000 | 正常关闭 - Normal Closure |
1001 | 表示一个端点已经不可用;譬如服务器down了,或者浏览器已经离开当前页面 - Going away |
1002 | 表示因为协议错误某个端点正在终止连接 - Protocol error |
1003 | 表示某个端点因为接收到不合法的数据类型而终止连接 - Unsupported Data |
1004 | 保留字 -- Reserved |
1005 | 保留值。它一定不能被设为Close控制frame中的状态码 - No Status Rcvd |
1006 | 保留值。它一定不能被设为Close控制frame中的状态码 - Abnormal Closure |
1007 | 表示某端因为接收到的数据与其消息类型不一致而中断连接 - Invalid frame payload data |
1008 | 表示某端因为接收到违背其策略的消息而中断连接;这是一个通用的状态码 - 当没有合适的状态码时就返回它 - Policy Vialation |
1009 | 表示某端因为接收到太大的消息包以致不能处理而中断连接 - Message too big |
1010 | 客户端终止连接,因为它希望服务器端返回一个乃至多个extension,但服务器端在WebSocket握手的返回消息里没有包含它 - Mandatory Ext. |
1011 | 表示服务器因为遇到了不可预期的情形以致不能响应请求所以中断连接 - Internal Server Error |
1015 | 保留值。它一定不能被设为Close控制frame中的状态码 - TLS handshake |
构建器:
[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 [TreatNonCallableAsNull] attribute Function? onopen; [TreatNonCallableAsNull] attribute Function? onerror; [TreatNonCallableAsNull] attribute Function? onclose; readonly attribute DOMString extensions; readonly attribute DOMString protocol; void close([Clamp] optional unsigned short code, optional DOMString reason); // messaging [TreatNonCallableAsNull] attribute Function? onmessage; attribute DOMString binaryType; void send(DOMString data); void send(ArrayBufferView data); void send(Blob data); };
readyState有四个取值:
- CONNECTING(0): 连接尚未建立
- OPEN(1): WebSocket连接已建立,可以进行通信
- CLOSING(2): 连接将要进行closing握手
- CLOSED(3): 连接已关闭或不能打开
websocket基于事件驱动,它包含以下四类事件:
onopen
onmessage
onclose
onerror
当有消息过来时,会触发onmessage事件。
exampleSocket.onmessage = function (event) { console.log(event.data); }
如何关闭连接:
exampleSocket.close();
但是在关闭连接前最好检查下bufferedAmount看看是不是还有数据没有发送完成。
浏览器支持
Feature | Chrome | Firefox(Gecko) | IE | Opera | Safari |
Version -76 support(Obsolete) | 6 | 4.0 (2.0) | -- | 11.00(disabled) | 5.0.1 |
Protocol version 7 support | -- | 6.0(6.0) | -- | -- | -- |
Protocol version 10 support | 14 | 7.0(7.0) | Html5 Labs | ? | ? |
RFC 6455 Support (IETF Draft 17) | 16 | 11.0(11.0) | 10 | ? | ? |
在FireFox 6.0-10.0中,需要使用MozWebSocket对象,而之后则是WebSocket对象。
参考实现
Java
参阅:WebSocketAPI
NodeJS
Ruby
Reference
1. WebSocket规范 http://tools.ietf.org/html/rfc6455
2. WebSocket API定义 http://dev.w3.org/html5/websockets/
5. WebSocket Vs. Rest http://nbevans.wordpress.com/2011/12/16/websockets-versus-rest-fight/
6. JAVA websocket实现 http://java.net/projects/websocket-spec/pages/WebSocketAPIs
相关推荐
柳木木的IT 2020-11-04
joynet00 2020-09-23
wenf00 2020-09-14
蓝色深海 2020-08-16
wuychn 2020-08-16
取个好名字真难 2020-08-06
darylove 2020-06-26
shufen0 2020-06-20
Lovexinyang 2020-06-14
WangBowen 2020-06-14
firejq 2020-06-14
hjhmpl 2020-06-14
水痕 2020-06-07
guozewei0 2020-06-06
woniyu 2020-06-02
取个好名字真难 2020-06-01
guozewei0 2020-05-28
woniyu 2020-05-26