WebSocket の概要 - ウェブへのソケットの導入

問題: 低レイテンシのクライアント / サーバー接続とサーバー / クライアント接続

ウェブは主に、いわゆる HTTP のいわゆるリクエスト/レスポンス パラダイムを中心に構築されています。クライアントがウェブページを読み込んでも、ユーザーが次のページをクリックするまで何も起こりません。AJAX は 2005 年頃から、ウェブをよりダイナミックに表現するようになりました。依然としてすべての HTTP 通信はクライアントによってステアリングされており、サーバーから新しいデータを読み込むにはユーザー操作や定期的なポーリングが必要でした。

新しいデータが利用可能になったときにサーバーがクライアントにデータを送信できるテクノロジーは、かなり昔から存在しています。「Push」や「Comet」などの名前が付けられています。サーバーが接続を開始したと錯覚する最も一般的なハッキングは、ロング ポーリングと呼ばれます。ロング ポーリングでは、クライアントはサーバーに対して HTTP 接続を開き、レスポンスを送信するまでサーバーを開いたままにします。サーバーが実際に新しいデータを取得するたびに、レスポンスを送信します(FlashXHR マルチパート リクエスト、いわゆる htmlfile などの手法もあります)。 ロング ポーリングやその他の手法もうまく機能します。Gmail チャットなどのアプリケーションで毎日使用しています。

ただし、これらの回避策のすべてに共通する問題が 1 つあります。それは、HTTP のオーバーヘッドが生じるため、低レイテンシ アプリケーションには適していないということです。ブラウザでのマルチプレーヤー型ファースト パーソン シューティング ゲームや、リアルタイム コンポーネントを使用するその他のオンライン ゲームを考えてみてください。

WebSocket の導入: ウェブにソケットを提供する

WebSocket 仕様では、ウェブブラウザとサーバー間の「ソケット」接続を確立する API が定義されています。つまり、クライアントとサーバーの間には永続的な接続があり、双方はいつでもデータの送信を開始できます。

スタートガイド

WebSocket コンストラクタを呼び出すだけで、WebSocket 接続が開きます。

var connection = new WebSocket('ws://html5rocks.websocket.org/echo', ['soap', 'xmpp']);

ws: に注目してください。これは、WebSocket 接続用の新しい URL スキーマです。安全な HTTP 接続で https: を使用するのと同様に、安全な WebSocket 接続用の wss: もあります。

いくつかのイベント ハンドラを接続に直ちにアタッチすると、接続が開かれたときや、受信メッセージを受け取ったとき、またはエラーが発生したときに通知を受け取ることができます。

2 番目の引数には、オプションのサブプロトコルを指定できます。文字列または文字列の配列を指定できます。各文字列はサブプロトコル名を表す必要があり、サーバーは配列で渡されたサブプロトコルのうち 1 つのみを受け入れます。受け入れられるサブプロトコルは、WebSocket オブジェクトの protocol プロパティにアクセスすることで確認できます。

サブプロトコル名は、IANA レジストリに登録されているサブプロトコル名のいずれかである必要があります。2012 年 2 月の時点で、現在登録されているサブプロトコル名(soap)は 1 つだけです。

// When the connection is open, send some data to the server
connection.onopen = function () {
connection.send('Ping'); // Send the message 'Ping' to the server
};

// Log errors
connection.onerror = function (error) {
console.log('WebSocket Error ' + error);
};

// Log messages from the server
connection.onmessage = function (e) {
console.log('Server: ' + e.data);
};

サーバーとの通信

サーバーに接続すると(open イベントが発生したら)、接続オブジェクトの send('your message') メソッドを使用してサーバーへのデータ送信をすぐに開始できます。以前は文字列のみをサポートしていましたが、最新の仕様ではバイナリ メッセージも送信できるようになりました。バイナリデータを送信するには、Blob オブジェクトまたは ArrayBuffer オブジェクトを使用します。

// Sending String
connection.send('your message');

// 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];
}
connection.send(binary.buffer);

// Sending file as Blob
var file = document.querySelector('input[type="file"]').files[0];
connection.send(file);

同様に、サーバーはいつでも Google にメッセージを送信できます。この状況が発生すると、onmessage コールバックが発生します。コールバックはイベント オブジェクトを受け取り、実際のメッセージには data プロパティを介してアクセスできます。

WebSocket は、最新の仕様のバイナリ メッセージを受信することもできます。バイナリ フレームは Blob または ArrayBuffer 形式で受信できます。受信するバイナリの形式を指定するには、WebSocket オブジェクトの binaryType プロパティを「blob」または「arraybuffer」に設定します。デフォルトの形式は「blob」です。(送信時に binaryType パラメータを調整する必要はありません)。

// Setting binaryType to accept received binary as either 'blob' or 'arraybuffer'
connection.binaryType = 'arraybuffer';
connection.onmessage = function(e) {
console.log(e.data.byteLength); // ArrayBuffer object if binary
};

WebSocket のもう一つの新しく追加された機能は拡張機能です拡張機能を使用すると、圧縮フレームや多重化フレームなどを送信できるようになります。サーバーが受け入れた拡張機能を見つけるには、オープン イベント後に WebSocket オブジェクトの拡張プロパティを調べます。2012 年 2 月の時点で、拡張機能の仕様は公式に発表されていません。

// Determining accepted extensions
console.log(connection.extensions);

クロスオリジン通信

最新のプロトコルであるクロスオリジン通信は、WebSocket に直接組み込まれています。やはり信頼できるクライアントとサーバーとのみ通信するようにする必要がありますが、WebSocket を使用すると任意のドメインのパーティ間の通信が可能になります。サーバーはサービスをすべてのクライアントに提供するか、明確に定義された一連のドメインに存在するクライアントのみが利用できるかを決定します。

プロキシ サーバー

新しいテクノロジーが登場するたびに、新たな問題が発生します。WebSocket の場合は、ほとんどの企業ネットワークで HTTP 接続を仲介するプロキシ サーバーとの互換性です。WebSocket プロトコルは、HTTP アップグレード システム(通常は HTTP/SSL に使用されます)を使用して、HTTP 接続を WebSocket 接続に「アップグレード」します。一部のプロキシ サーバーはこれを処理せず、接続を切断します。したがって、特定のクライアントが WebSocket プロトコルを使用しても、接続を確立できないことがあります。これにより、次のセクションはさらに重要なものになります。

今すぐ WebSocket を使用する

WebSocket はまだ新しいテクノロジーであり、すべてのブラウザに完全には実装されていません。ただし、WebSocket が使用できない場合は、上記のフォールバックのいずれかを使用するライブラリで、現在 WebSocket を使用できます。この分野で広く普及しているライブラリは socket.io です。これは、プロトコルのクライアントとサーバーの実装に付属し、フォールバックを含みます(2012 年 2 月時点で socket.io はバイナリ メッセージングをサポートしていません)。また、WebSocket メッセージをクライアントに送信するための HTTP API を提供することで、任意のウェブ環境に簡単に統合できる PusherApp などの商用ソリューションもあります。余分な HTTP リクエストにより、純粋な WebSocket と比較して常に余分なオーバーヘッドが発生します。

サーバーサイド

WebSocket を使用することで、サーバー側のアプリケーションにまったく新しい使用パターンが生まれます。LAMP などの従来のサーバー スタックは、HTTP リクエスト/レスポンス サイクルを中心に設計されていますが、多くのオープン WebSocket 接続を適切に処理できません。多数の接続を同時に開いたままにするには、低いパフォーマンス コストで高い同時実行性に対応できるアーキテクチャが必要です。このようなアーキテクチャは通常、スレッドまたはいわゆるノンブロッキング IO を中心として設計されています。

サーバーサイドの実装

プロトコル バージョン

WebSocket のワイヤ プロトコル(クライアントとサーバー間の handshake とデータ転送)は RFC6455 になりました。最新の Chrome と Chrome for Android は、バイナリ メッセージングを含む RFC6455 に完全に対応しています。また、Firefox はバージョン 11 から、Internet Explorer はバージョン 10 からサポートされるようになります。古いバージョンのプロトコルを引き続き使用することもできますが、脆弱であることがわかっているため、この方法はおすすめしません。古いバージョンの WebSocket プロトコルのサーバー実装がある場合は、最新バージョンにアップグレードすることをおすすめします。

ユースケース

クライアントとサーバー間で真に低レイテンシで準リアルタイムの接続が必要な場合は、WebSocket を使用します。その際には、イベントキューなどのテクノロジーに着目して、サーバーサイド アプリケーションの構築方法を再考する必要があるかもしれません。ユースケースの例:

  • マルチプレーヤー型オンライン ゲーム
  • チャット アプリケーション
  • ライブ スポーツ ティッカー
  • ソーシャル ストリームのリアルタイム更新

デモ

リファレンス