socket in js

socket 如今也出現在了 javascript 中,瀏覽器端有 html5 規範的 websocket 來完成客戶端信息發送,服務器端則可以在 nodejs 平臺上實現對應的服務器信息接收 (原生支持的異步io讀取),而另一方面客戶端 api 由 w3c 制定,具體的通信協議則由 IETF 指定,目前尚處於草稿階段,最新的規範也已過期。

 

 

客戶端發送:

 

目前只有 chrome 支持 websocket ,通過

 

var webSocket = new WebSocket('ws://xx.com:8081/');

 

 來創建客戶端 socket 對象,而具體通信協議則是採用更早的 draft-ietf-hybi-thewebsocketprotocol-00

 

通過 send 方法來發送 utf-8 編碼的文本信息(二進制上尚不支持)。通過 onopen , onmessage 回調來獲取服務器端的對應返回狀態和數據。

 

基本原理:

 

首先是握手階段(handshake):瀏覽器發送 http get 請求到對應 xx.com:8081 ,並攜帶對應協議頭 :

 

 

 

注意最後的 (key3) 並不表示一個 http header,而是在頭結束後第二行的8個字節爲 0x7D 0x24 ....,這8字節不屬於 header 部分,由於是 get 請求,也不屬於 body部分,造成一般的 web 容器經過層層封裝後應用根本讀不出來。

 

服務器端然後設置返回狀態碼 101 ,並根據 sec-websocket-key1/2 以及最後的 8 個字節,計算出另外一個字符串回來確認(challenge response 爲了安全?):

 

 

 

 

至此,握手完畢,該 http 鏈接的 tcp 鏈接保留,以後數據傳輸直接通過 socket 進行(也即提升爲 websocket),不過數據開頭以 0x00 開頭,以 0xFF 結尾,服務器端也是如此,只不過瀏覽器端會自動進行數據解包,因而對應用透明。

 

 

服務器端接收:

 

 

服務器實際上是一個特殊的 web (http)服務器,不同於一般的 http server,應用完全不需要了解 http 底層的 tcp socket,在 websocket 請求時就要適當暴漏承載 http 鏈接的 tcp socket,所以目前的 servlet 容器例如 tomcat就不行了(不可能從 httpRequest 直接得到它關聯的 socket,被 tomcat 屏蔽了,另一個原因是不能讀到請求的 raw data 造成 key3 讀不到),這纔有了 jetty 經過改造後支持 websocket 的 servlet 容器,或者直接手寫 websocket 專用服務器

 

nodejs 下實現

 

nodejs 的 http server 已經支持 websocket 的握手階段,能夠將握手階段作爲事件觸發出來,並且讀出 key3,一旦到了握手階段,該 socket 就會暴露出來 :

 

 server.on('upgrade', function(req, socket, upgradeHead) {    });

 

這時應用就可以設置 socket 爲 keepalive 一直保持打開狀態:

 

socket.setKeepAlive(true, 0);

 

接下來就是讀取對應的 key1 和 key2 以及 key3,計算需要返回的確認值,以及準備其他必要的信息頭(origin,location...)發送到客戶端

 

res = 'HTTP/1.1 101 WebSocket Protocol Handshake\r\n' +
          'Upgrade: WebSocket\r\n' +
          'Connection: Upgrade\r\n' +
          'Sec-WebSocket-Origin: ' + websocket_origin(connection) + '\r\n' +
          'Sec-WebSocket-Location: ' + location;
.......
connection._socket.write(res, 'binary');
 

完成了握手階段後,客戶端會直接通過 socket 發送信息,而服務器也會經過服務器端socket的data 事件來獲取客戶端信息處理:

 

socket.on('data', function(data) {
//todo
});
 

服務器通過 socket 的 write 來向客戶端瀏覽器發送信息,這時也要將數據用 0x00 和 0xff 包裹起來:

 

Connection.prototype.write = function(data) {
   return this.socket.write( '\u0000' + data + '\uffff');
};

 問題:

 

使用 websocket 使得客戶端和服務器端都可以重用同一個 tcp 鏈接,不用頻繁建立銷燬 http 鏈接,並且可以避免採用 http 請求冗餘的頭信息,大幅提高反應速度。但是估計會降低可訪問的客戶端數,畢竟服務器鏈接有限,都被早來的客戶端佔完了:(

 

 

 

Refer:

 

 

flash 模擬的 websocket

nodejs 的 websocket 服務器模塊

websocket 介紹

客戶端html5 api w3c規範

websocket IETF通信協議

如何通過握手建立連接

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章