WebSocket原理與實踐(二)---WebSocket協議

WebSocket原理與實踐(二)---WebSocket協議javascript

    WebSocket協議是爲了解決web即時應用中服務器與客戶端瀏覽器全雙工通訊問題而設計的。協議定義ws和wss協議,分別爲普通請求和基於SSL的安全傳輸, ws端口是80,wss的端口爲443.html

WebSocket協議由兩部分組成,握手和數據傳輸。java

2-1 握手
WS的握手使用HTTP來實現的。客戶端的握手消息是一個普通的,帶有Upgrade頭的,HTTP Request的消息。
先來看看以下代碼:node

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>websocket</title>
    <meta name="viewport" content="width=device-width, initial-scale=1"/>
  </head>
  <body>
    <script type="text/javascript">
      var wsUrl = "wss://echo.websocket.org";
      var ws = new WebSocket(wsUrl);
      ws.onopen = function() {
        console.log('open');
      };
      ws.onmessage = function(msg) {
        console.log(msg.data);
      }
      ws.onclose = function() {
        console.log('已經被關閉了');
      }
    </script>
  </body>
</html>

頁面運行後,咱們能夠看到連接到 wss://echo.websocket.org 期間記錄的一個握手協議。先來看看客戶端發送http的請求頭:git

GET /chat HTTP/1.1
Host:echo.websocket.org
Upgrade:websocket
Connection:Upgrade
Sec-WebSocket-Key:ALS2AoBJtUup67heKDgzFg==
Origin:file://
Sec-WebSocket-Version:13

服務器響應的頭字段github

Connection:Upgrade
Sec-WebSocket-Accept:qyzx/EgbRK15QNmr5PhpMQrPZMM=
Server: Kaazing Gateway
Upgrade:websocket

下面是請求和響應頭字段的含義:
Upgrade: websocket, 告訴服務器這個HTTP連接是升級的WebSocket協議。
Connection:Upgrade 告知服務器當前請求連接是升級的。
Origin: 該字段是用來防止客戶端瀏覽器使用腳本進行未受權的跨源攻擊,服務器要根據這個字段是否接受客戶端的socket連接。
能夠返回一個HTTP錯誤狀態碼來拒絕鏈接。web

Sec-WebSocket-Key: ALS2AoBJtUup67heKDgzFg==
Sec-WebSocket-Accept: qyzx/EgbRK15QNmr5PhpMQrPZMM=算法

Sec-WebSocket-Key 的值是一串長度爲24的字符串是客戶端隨機生成的base64編碼的字符串,它發送給服務器,服務器須要使用它通過必定的運算規則生成服務器的key,而後把服務器的key發到客戶端去,客戶端驗證正確後,握手成功。瀏覽器

握手的具體原理:當咱們客戶端執行 new WebSocket(''wss://echo.websocket.org')的時候,客戶端就會發起請求報文進行握手申請,報文中有一個key就是
Sec-WebSocket-Key,服務器獲取到key,會將這個key與字符串258EAFA5-E914-47DA-95CA-C5AB0DC85B11相連,對新的字符串經過sha1安全散列算法計算出結果後,再進行Base64編碼,而且將結果放在請求頭的"Sec-WebSocket-Accept",最後返回給客戶端,
客戶端進行驗證後,握手成功。握手成功後就能夠開始數據傳輸了。安全

下面是實現一個簡單的握手協議的demo,代碼以下:

### 目錄結構以下:

demo
  |--- hands.html
  |--- hands.js

hands.html 代碼以下:

<html>
<head>
  <title>WebSocket Demo</title>
</head>
<body>
  <script type="text/javascript">
    var ws = new WebSocket("ws://127.0.0.1:8000");
    ws.onerror = function(e) {
      console.log(e);
    };
    ws.onopen = function() {
      console.log('握手成功');
    }
  </script>
</body>
</html>

hands.js 代碼以下:

var crypto = require('crypto');

var WS = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';

require('net').createServer(function(o) {
  var key;
  o.on('data', function(e) {
    if (!key) {
      console.log(e);

      key = e.toString().match(/Sec-WebSocket-Key: (.+)/)[1];
      console.log(key);
      
      // WS的字符串 加上 key, 變成新的字符串後作一次sha1運算,最後轉換成Base64
      key = crypto.createHash('sha1').update(key+WS).digest('base64');
      console.log(key);

      // 輸出字段數據,返回到客戶端,
      o.write('HTTP/1.1 101 Switching Protocol\r\n');
      o.write('Upgrade: websocket\r\n');
      o.write('Connection: Upgrade\r\n');
      o.write('Sec-WebSocket-Accept:' +key+'\r\n');
      // 輸出空行,使HTTP頭結束
      o.write('\r\n');
    } else {
      // 數據處理
    }
  })
}).listen(8000);

首先在命令行中 進入相對應項目目錄後,運行 node hands.js, 而後打開 hands.html 運行一下便可看到 命令行中打印出來以下信息:

$ node hands.js
<Buffer 47 45 54 20 2f 20 48 54 54 50 2f 31 2e 31 0d 0a 48 6f 73 74 3a 20 31 32 37 2e 30 2e 30 2e 31 3a 38 30 30 30 0d 0a 43 6f 6e 6e 65 63 74 69 6f 6e 3a 20 ... >
+iHlfGTolBaWYpnyTIw22g==
W7IEsdQtwv8EP2204kssK/6pg+c=

而後在瀏覽器中查看請求頭以下信息:

Request Headers:

Connection:Upgrade
Host:127.0.0.1:8000
Origin:file://
Sec-WebSocket-Extensions:permessage-deflate; client_max_window_bits
Sec-WebSocket-Key:+iHlfGTolBaWYpnyTIw22g==
Sec-WebSocket-Version:13
Upgrade:websocket

響應頭以下信息:

Response Headers:

Connection:Upgrade
Sec-WebSocket-Accept:W7IEsdQtwv8EP2204kssK/6pg+c=
Upgrade:websocket

如上信息能夠看到,獲取報文中的key代碼:
key = e.toString().match(/Sec-WebSocket-Key: (.+)/)[1];
console.log(key); // 打印 +iHlfGTolBaWYpnyTIw22g==

和 Request Headers:中的 Sec-WebSocket-Key 值是同樣的,該值是瀏覽器自動生成的,而後獲取該值後,與 '258EAFA5-E914-47DA-95CA-C5AB0DC85B11',相連,對新的字符串經過sha1安全散列算法計算出結果後,再進行Base64編碼,
而且將結果放在請求頭的"Sec-WebSocket-Accept",最後返回給客戶端,客戶端進行驗證後,握手成功。在瀏覽器中能夠看到打印出 握手成功了。

github上查看demo

相關文章
相關標籤/搜索