這個是一次組內分享,關於websocket的協議和應用的。文章在分享之前就寫好了,整理下放出來。
對應的PPT地址是:http://websocket.funaio.com
從推送技術開始說
一篇文章10 Years of Push Technology, Comet, and WebSockets(http://cometdaily.com/2011/07/06/push-technology-comet-and-websockets-10-years-of-history-from-lightstreamers-perspective/)非常詳細的說明清楚了從1996-2007年推送技術的更新。2000年之前為第一波Push技術,使用的概念叫Webcasting。大致思想就是用戶來服務端注冊一個或者多個通道channel,然后服務端確定給某些個channel或某個channel發送消息。2000到2007年最火的詞叫comet,比如有Polling(這個是最普通的輪詢),Long Polling(hold住HTTP的response端,當有消息的時候才返回),至於HTTP發起是在不同的哪些地方,Ajax,Flash,Iframe,然后就有各自不同的叫法和名詞。但是不管什么技術,都限制於瀏覽器,因為瀏覽器只能發起HTTP請求。但是推送最好的一種方法當然是保持Socket長連接,瀏覽器如何能發起TCP連接呢?HTML5的Websocket技術就解決了這個問題。
websocket概述
現狀
websocket的版本發展:
draft-hixie-00
draft-hixie-76
draft-hybi-00
draft-hybi-17
RFC 6455
所以有的地方會出現hixie,hybi的字樣,純粹是為了滿足之前的版本。好消息是從2011年12月份開始,websocket的RFC正式版出來了。
客戶端現狀
從客戶端來講,大部分瀏覽器支持的是hybi-13版本,比如我使用的chrome v24,firefox v16。相信各家瀏覽器都很快能出現支持RFC版本的瀏覽器了。網上有很多文章說“支持websocket的瀏覽器”,這個說法其實是不對的,至少是不完整的。比如Chrome從草案開始就已經支持websocket了,你拿一個支持hixie76版本的chrome調用實現了RFC 6455的服務端,那是怎么樣也對不上的。所以說“支持websocket的瀏覽器”還應該多帶一句,支持什么版本。
瀏覽器來說還分為手機瀏覽器和PC瀏覽器。手機瀏覽器特別是android上的瀏覽器版本支持websocket的比PC上少很多(這可能和大家幾乎不會去定期更新手機瀏覽器有關)。網上對那些版本瀏覽器支持那些版本的websocket有好多文章進行統計了。但是由於瀏覽器種類繁多,版本之多,記住這些版本,不如在定義需求的時候明確我需要支持哪些瀏覽器的哪些版本,然后寫一個簡單例子試一下更為准確。
服務端現狀
下面是服務端。服務端對websocket的語言開發已經是沒有問題了。http://en.wikipedia.org/wiki/WebSocket wiki上列出了各種服務端語言開發websocket的第三方包了。可以直接拿來用,也可以根據協議自己開發個websocket包,比如http://www.cnblogs.com/yjf512/archive/2013/02/18/2915171.html。甚至於像apache,nginx(1.3.13)這些現成的web服務器產品也都漸漸支持websocket了(nginx現在只是支持nginx代理)。
socket.io這個包是node上用的最多的一個第三方包。它其實並不僅僅是websocket。官方的faq http://socket.io/#faq 也說明清楚了為什么它不直接叫“websocket”包。socket.io的js包可以給客戶端使用,也可以給服務端使用,客戶端使用socket.io能很簡單建立websocket連接,但是上,支持websocket的瀏覽器是不需要使用socket.io來建立websocket連接的。在服務端,使用socket.io並不是只能創建websocket服務器,就是說socket.io要建立的服務是“socket服務”而不僅僅是“websocket服務”。
websocket的原理
websocket是一種協議,本質上和http,tcp一樣。協議是用來說明數據是如何傳輸的。它的url前綴是ws:// 或者wss://,后者是加密的websocket。它的url諸如這樣:ws://10.16.15.64:3201/
客戶端和服務端進行websocket交互的方式也有人理解為“HTTP握手+TCP數據傳輸”的方式。
HTTP握手+TCP數據傳輸
握手和傳輸的整個流程是這樣的:
瀏覽器(支持Websocket的瀏覽器)像HTTP一樣,發起一個請求,然后等待服務端的響應
服務器返回握手響應,告訴瀏覽器請將后續的數據按照websocket制定的數據格式傳過來
瀏覽器和服務器的socket連接不中斷,此時這個連接和http不同的是它是雙工的了
瀏覽器和服務器有任何需要傳遞的數據的時候使用這個長連接進行數據傳遞
這里說它是HTTP握手,是因為瀏覽器和服務器在建立長連接的握手過程是按照HTTP1.1的協議發送的,有Request,Request Header, Response, Response Header。但是不同的是Header里面的字段是有特定含義的。
說它是TCP傳輸,主要體現在建立長連接后,瀏覽器是可以給服務器發送數據,服務器也可以給瀏覽器發送請求的。當然它的數據格式並不是自己定義的,是在要傳輸的數據外層有ws協議規定的外層包的。
握手過程
這是一個握手過程的例子。
Upgrade頭表示的意思是“客戶端除了http之外也支持websocket協議,而且更傾向使用websocket,服務端如果支持的話,咱們就換websocket協議吧”
sec-websocket-version:是指出瀏覽器支持的websocket號。這里是支持hybi-13。這里是不會出現9-12的版本號的。websocket協議規定9-12是保留字段。
sec-websocket-key:算是一種驗證返回回來的服務端是否是支持websocket的驗證算法。與Response中的sec-websocket-accept是對應的。
sec-websocket-accept與sec-websocket-key的對應算法是:
sec-websocket-accept = base64(hsa1(sec-websocket-key + 258EAFA5-E914-47DA-95CA-C5AB0DC85B11))
如果返回的sec-websocket-accept不對,在chrome下會出現Sec-WebSocket-Accept dismatch的錯誤。
Response返回的HTTP Staus是101,代表服務端說“我們雙方后面就按照websocket協議來進行數據傳輸吧”
數據傳輸過程
websocket的數據傳輸是frame形式傳輸的,比如會將一條消息分為幾個frame,按照先后順序傳輸出去。這樣做會有幾個好處:
1 大數據的傳輸可以分片傳輸,不用考慮到數據大小導致的長度標志位不足夠的情況。
2 和http的chunk一樣,可以邊生成數據邊傳遞消息,即提高傳輸效率。
傳輸協議:
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位,任意的應用數據,放在擴展數據之后,應用數據的長度=負載數據的長度-擴展數據的長度
讀取數據需要按照這個格式讀取,發送數據也需要按照這個格式發送返回。
代碼示例:
http://www.cnblogs.com/yjf512/archive/2013/02/18/2915171.html
應用:
1 chatofPemelo
聊天室
https://github.com/NetEase/chatofpomelo
2 osxair
遠程控制air
https://github.com/jianfengye/MyWorks/tree/master/osxair
3 nginx1.3.13
nginx1.3.13只是支持了websocket代理
Feature: support for proxying of WebSocket connections.
參考文章
http://m.udpwork.com/item/6758.html
http://www.websocket.org/aboutwebsocket.html
http://tools.ietf.org/html/rfc6455
http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-13
http://www.cnblogs.com/yjf512/archive/2013/02/18/2915171.html