HTML5 webSocket

9732 ワード

一、リアルタイムweb応用の現状
WebSocket仕様が登場する前に、開発者はこれらのリアルタイムのWebアプリケーションを実現するために、いくつかのトレードオフ案を採用しなければならない.その中で最もよく使われているのはポーリング(Polling)とComet技術であり、Comet技術は実際にポーリング技術の改善であり、2つの実現方式に細分化することができる.1つはロングポーリングメカニズムであり、1つはストリーミング技術と呼ばれる.
  1.ポーリング
これは、リアルタイムのWebアプリケーションを実現するための最初のスキームです.クライアントは、クライアントとサーバの同期を維持するために頻繁に要求されるように、サービス側に一定の時間間隔で要求を発行する.この同期スキームの最大の問題は、クライアントが一定の周波数でサーバに要求を開始すると、サーバ側のデータが更新されない可能性があり、無駄なネットワーク転送が多くなるため、非常に非効率なリアルタイムスキームである.
  2.ロングポーリング
ロングポーリングは、無効なネットワーク伝送を低減するために、タイミングポーリングの改善と向上である.サーバ側にデータ更新がない場合、接続は、データまたはステータスが変化したり、期限が切れたりするまで一定の期間維持されます.このメカニズムにより、無効なクライアントとサーバ間のインタラクションが減少します.もちろん,サービス側のデータ変更が非常に頻繁であれば,このメカニズムはタイミングポーリングと比較して本質的な性能の向上はない.
  3.ながれ
ストリーム・テクノロジー・スキームは、通常、クライアントのページで非表示のウィンドウを使用して、サービス側に長い接続を要求する.サーバ側は、この要求に応答し、クライアント側とサーバ側の接続が期限切れにならないように接続状態を更新し続けます.このようなメカニズムにより、サーバ側の情報をクライアントに絶えずプッシュすることができる.このメカニズムはユーザー体験に問題があり、異なるブラウザに対して異なる方案を設計してユーザー体験を改善する必要がある.同時に、このメカニズムは同時比較的大きな状況で、サーバー側の資源に対して大きな試練である.
  4.上記案が露呈した問題
これらのシナリオを総合すると、現在使用されているリアルタイムテクノロジーとは真のリアルタイムテクノロジーではなく、Ajax方式でリアルタイムの効果をシミュレートするだけで、クライアントとサーバが相互作用するたびにHTTPのリクエストと応答のプロセスであることがわかります.一方、HTTPリクエストと応答ごとに完全なHTTPヘッダ情報が含まれているため、送信ごとのデータ量が増加し、これらのスキームではクライアント側とサーバ側のプログラミング実装が複雑であり、実際のアプリケーションでは、よりリアルなリアルタイム効果をシミュレートするために、開発者は往々にして2つのHTTP接続を構築してクライアントとサーバ間の双方向通信をシミュレートする必要があり、1つの接続はクライアントからサーバ側へのデータ伝送を処理するために使用され、1つの接続はサーバ側からクライアントへのデータ伝送を処理するために使用され、これはプログラミング実現の複雑さを増大させ、サーバ側の負荷を増加させることは避けられない.応用システムの拡張性を制約した.
二、webSocketとは
WebSocket協定は2008年に誕生し、2011年に国際標準となった.すべてのブラウザがサポートされています.WebSocketプロトコルは本質的にTCPベースのプロトコルである.WebSocket接続を確立するために、クライアントブラウザはまずサーバにHTTP要求を開始しなければならない.この要求は通常のHTTP要求とは異なり、いくつかの付加ヘッダ情報が含まれており、付加ヘッダ情報「Upgrade:WebSocket」は、申請プロトコルが昇格したHTTP要求であることを示している.サーバ側はこれらの付加的なヘッダ情報を解析して応答情報を生成してクライアントに返し、クライアント側とサーバ側のWebSocket接続が確立され、双方はこの接続チャネルを通じて自由に情報を伝達することができ、この接続はクライアント側またはサーバ側のいずれかがアクティブに接続を閉じるまで持続する.
三、webScoketの特徴
(1)TCPプロトコル上に構築され、サーバ側の実現が比較的容易である
(2)HTTPプロトコルとの互換性が良好である.デフォルトポートも80と443であり、握手段階ではHTTPプロトコルが採用されているため、握手時にマスクしにくく、各種HTTPプロキシサーバを介して
(3)データフォーマットが比較的軽量で、性能コストが小さく、通信効率が高い
(4)テキスト送信もバイナリデータ送信も可能
(5)同源制限なし、クライアントは任意のサーバと通信可能
(6)プロトコル識別子はws(暗号化されている場合はwss)であり、サーバURLはURLである
四、webSocket例
典型的なWebSocketがリクエストを開始し、応答を得た例は次のように見えます.
       : 
GET /demo HTTP/1.1 
Host: example.com 
Connection: Upgrade 
Sec-WebSocket-Key2: 12998 5 Y3 1 .P00 
Upgrade: WebSocket 
Sec-WebSocket-Key1: 4@1 46546xW%0l 1 5 
Origin: http://example.com 
[8-byte security key] 
 
       :
HTTP/1.1 101 WebSocket Protocol Handshake 
Upgrade: WebSocket 
Connection: Upgrade 
WebSocket-Origin: http://example.com 
WebSocket-Location: ws://example.com/demo 
[16-byte hash response]

「Upgrade:WebSocket」は、クライアント側とサーバ側の通信プロトコルをHTTPプロトコルからWebSocketプロトコルにアップグレードするための特別なHTTPリクエストであることを示しています.クライアントからサーバ側に要求された情報には、「Sec-WebSocket-Key 1」、「Sec-WebSocket-Key 2」および「[8-byte securitykey]」といったヘッダ情報が含まれている.これはクライアントブラウザがサーバ側に提供する握手情報であり、サーバ側はこれらのヘッダ情報を解析し、握手の過程でこれらの情報に基づいて16ビットのセキュリティ鍵を生成し、クライアントに返し、サーバ側がクライアントの要求を取得し、WebSocket接続の作成に同意したことを示す.接続が確立されると、クライアント側とサーバ側はこのチャネルを介してデータを双方向に転送することができる.
五、クライアントAPI
  1.WebSocketコンストラクタ
WebSocketオブジェクトは、WebSocketインスタンスを新規作成するコンストラクション関数として使用されます.次の文を実行すると、クライアントはサーバに接続されます.
var ws = new WebSocket('ws://localhost:8080');

  2.webSocket.readyState readyStateプロパティは、インスタンスオブジェクトの現在の状態を返します.4つです.
(1)connecting:値が0で、接続中であることを示します.
(2)open:値は1で,接続に成功し,通信可能になったことを示す.
(3)closing:値が2で、接続が閉じていることを示します.
(4)closed:値は3で、接続が閉じているか、接続が開いていないことを示します.
例:
switch (ws.readyState) {
  case WebSocket.CONNECTING:
    // do something
    break;
  case WebSocket.OPEN:
    // do something
    break;
  case WebSocket.CLOSING:
    // do something
    break;
  case WebSocket.CLOSED:
    // do something
    break;
  default:
    // this never happens
    break;
}

  3.webSocket.onopen
インスタンスオブジェクトのonopenプロパティ.接続に成功したコールバック関数を指定します.
ws.onopen = function () {
  ws.send('Hello Server!');
}

複数のコールバック関数を指定する場合は、addEventListenerメソッドを使用します.
ws.addEventListener('open', function (event) {
  ws.send('Hello Server!');
});

   4.webSocket.onclose
インスタンスオブジェクトのoncloseプロパティで、接続が閉じたコールバック関数を指定します.
ws.onclose = function(event) {
  var code = event.code;
  var reason = event.reason;
  var wasClean = event.wasClean;
  // handle close event
};

ws.addEventListener("close", function(event) {
  var code = event.code;
  var reason = event.reason;
  var wasClean = event.wasClean;
  // handle close event
});

   5.websocket.onmessage
インスタンス・オブジェクトのonmessageプロパティ.サーバ・データを受信したコールバック関数を指定します.
ws.onmessage = function(event) {
  var data = event.data;
  //     
};

ws.addEventListener("message", function(event) {
  var data = event.data;
  //     
});

なお、サーバデータは、テキストであってもバイナリデータであってもよい(blobオブジェクトまたはArraybufferオブジェクト).
ws.onmessage = function(event){
  if(typeof event.data === String) {
    console.log("Received data string");
  }

  if(event.data instanceof ArrayBuffer){
    var buffer = event.data;
    console.log("Received arraybuffer");
  }
}

受信したデータ型を動的に判断するほか、binaryType属性を使用して、受信したバイナリデータ型を明示的に指定することもできる.
//      blob   
ws.binaryType = "blob";
ws.onmessage = function(e) {
  console.log(e.data.size);
};

//      ArrayBuffer   
ws.binaryType = "arraybuffer";
ws.onmessage = function(e) {
  console.log(e.data.byteLength);
};

   6.websocket.send()
インスタンスオブジェクトのsend()メソッドは、サーバにデータを送信するために使用されます.   
       。
ws.send('your message');

   Blob      。
var file = document
  .querySelector('input[type="file"]')
  .files[0];
ws.send(file);

   ArrayBuffer      。
// Sending canvas ImageData as ArrayBuffer
var img = canvas_context.getImageData(0, 0, 400, 320);
var binary = new Uint8Array(img.data.length);
for (var i = 0; i < img.data.length; i++) {
  binary[i] = img.data[i];
}
ws.send(binary.buffer);

  7.websocket.bufferedamount
インスタンスオブジェクトのbufferedAmount属性は、まだ何バイトのバイナリデータが送信されていないかを示す.送信が終了したかどうかを判断するために使用できます.
var data = new ArrayBuffer(10000000);
socket.send(data);

if (socket.bufferedAmount === 0) {
  //     
} else {
  //       
}

  8.websocket.onerror
インスタンスオブジェクトのonerrorプロパティで、エラーが発生したときのコールバック関数を指定します.
socket.onerror = function(event) {
  // handle error event
};

socket.addEventListener("error", function(event) {
  // handle error event
});

六、ノードサービス側の例
var WebSocketServer = require('websocket').server;
var http = require('http');

var server = http.createServer(function(request, response) {
    console.log((new Date()) + ' Received request for ' + request.url);
    response.writeHead(404);
    response.end();
});
server.listen(8080, function() {
    console.log((new Date()) + ' Server is listening on port 8080');
});

wsServer = new WebSocketServer({
    httpServer: server,
    // You should not use autoAcceptConnections for production
    // applications, as it defeats all standard cross-origin protection
    // facilities built into the protocol and the browser.  You should
    // *always* verify the connection's origin and decide whether or not
    // to accept it.
    autoAcceptConnections: false
});

function originIsAllowed(origin) {
  // put logic here to detect whether the specified origin is allowed.
  return true;
}

wsServer.on('request', function(request) {
    if (!originIsAllowed(request.origin)) {
      // Make sure we only accept requests from an allowed origin
      request.reject();
      console.log((new Date()) + ' Connection from origin ' + request.origin + ' rejected.');
      return;
    }
    
    var connection = request.accept('echo-protocol', request.origin);
    console.log((new Date()) + ' Connection accepted.');
    connection.on('message', function(message) {
        if (message.type === 'utf8') {
            console.log('Received Message: ' + message.utf8Data);
            connection.sendUTF(message.utf8Data);
        }
        else if (message.type === 'binary') {
            console.log('Received Binary Message of ' + message.binaryData.length + ' bytes');
            connection.sendBytes(message.binaryData);
        }
    });
    connection.on('close', function(reasonCode, description) {
        console.log((new Date()) + ' Peer ' + connection.remoteAddress + ' disconnected.');
    });
});

参照ドキュメント:1.http://www.ruanyifeng.com/blog/2017/05/websocket.html
     2.https://www.ibm.com/developerworks/cn/web/1112_huangxa_websocket/index.html
     3.https://developer.mozilla.org/zh-CN/docs/Web/API/WebSocket
     4.https://blog.csdn.net/li_jia_wei/article/details/81148053