HOME > ソフテックだより > 第173号(2012年11月7日発行) 技術レポート「「WebSocket」を利用したWebサーバ・Webブラウザ間の通信」

「ソフテックだより」では、ソフトウェア開発に関する情報や開発現場における社員の取り組みなどを定期的にお知らせしています。
さまざまなテーマを取り上げていますので、他のソフテックだよりも、ぜひご覧下さい。

ソフテックだより(発行日順)のページへ
ソフテックだより 技術レポート(技術分野別)のページへ
ソフテックだより 現場の声(シーン別)のページへ


ソフテックだより 第173号(2012年11月7日発行)
技術レポート

「「WebSocket」を利用したWebサーバ・Webブラウザ間の通信」

1. はじめに

ソフテックでは機器の制御・監視を目的とした専用のソフトの開発をしています。PC・組み込みマイコン・PLCなど、最適と思われる構成で提案・構築しています。PCでソフトを作成する場合でも機器に合わせた専用の画面を作成し、その画面上で操作・確認することで、機器の制御・監視を実現しています。
筆者自身は、弊社のSCADA製品であるFAVIEWの開発にも携わっています。
昨今、『専用のPC以外からでも監視制御したい。』『ホームページはどこからでも見られるのだから同じようにできないか?』などのご要望が増えてきました。都度、要望に対応してきておりましたが、最近は汎用的に使える通信技術として「WebSocket」が注目されています。今回は、この「WebSocket」を取り上げたいと思います。

2. 「WebSocket」とは

「WebSocket」とはWebサーバ・Webブラウザ間の通信技術のひとつで、同様の技術としてはXMLHttpRequestやCometなどがあります。これらの技術との大きな違いは、双方向にプッシュ通信が可能なことです。XMLHttpRequestやCometでもできないわけではありませんが、無理やり・擬似的といった実現方法となっています。
また、単に通信をしたいということであれば、ブラウザ用に独自のプラグインを開発し、組み込むということでも実現可能とは思います。しかし、この場合、ブラウザやOS毎にプラグインの開発が必要であり、開発/メンテナンス費用が大きくなってしまいます。
「WebSocket」は、インターネット技術タスクフォース - Internet Engineering Task Force(以下、IETF)によって標準化(RFC 6455)が進められており、現時点で幾つものブラウザで特別な対応なく標準として使用可能となっています。

3. 環境構築

実際に「WebSocket」を利用し、チャットアプリを作成しました。それぞれ以下の環境で構築しています。
以下、図示します。

構成
図1. 構成

○サーバ側
Visual C# 2010 Expressを利用し、HTTP サーバと「WebSocket」サーバの両方の機能を具備したアプリケーションを開発しました。運用環境は、Windows 7 Professionalを想定していますが、.NET Framework動作可能な環境であれば、問題なく動作する見込みです。

○クライアント側
Htmlファイル内JavaScriptで実装しました。Windows版 Chrome バージョン 22.0.1229.94 mおよびAndroid版Chrome バージョン 18.0.1025308で動作確認しています。

4. 開発 - 接続

Webブラウザからの要求が「WebSocket」の要求であると判断した時のみ接続を「WebSocket」に切り替えます。
Webブラウザからサーバへ特定のページを開くよう要求を行います。サーバ側は通常、HTTPサーバとして動作していますので、普通にウェブページが開きます。そのウェブページのhtmlファイル内にJavaScriptでスクリプトを記述しておくことで「WebSocket」の通信を始めます。

接続の処理は、以下のコードとなります。
基本的には、「WebSocket」のオブジェクトを構築するのみです。第一引数は通信先、第二引数はプロトコル識別の文字列です。

var adr = "ws://" + location.host + "/chat";
var webSocket = new WebSocket(adr, "chat");

上記オブジェクトの生成を行うとブラウザから以下のデータがサーバへ送信されます。

GET /chat HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: localhost
Origin: http://localhost
Sec-WebSocket-Protocol: chat
Sec-WebSocket-Key: qC+NLiI2OLcyJoKAl7bGDg==
Sec-WebSocket-Version: 13
Sec-WebSocket-Extensions: x-webkit-deflate-frame

サーバではこれを「WebSocket」のデータであると判断し、処理を「WebSocket」に切り替えます。今回はデータ中のUpgrade: websocketの有無で判断するようにしました。
「WebSocket」と判断した場合には決められた応答を返す必要があります。
応答データは以下の内容となります。

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-Websocket-Accept:ywGzZH9GlH09lFT8zPTzQSJpofU=

応答のSec-Websocket-Acceptのデータ(青文字部分)は、要求のSec-Websocket-Keyのデータ(赤文字部分)から生成します。
生成手順はSec-Websocket-Keyのデータの後ろに258EAFA5-E914-47DA-95CA-C5AB0DC85B11を連結し、SHA1でハッシュ化したものをBASE64でテキストにします。
以下のコードになります。

SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider();
byte [] keyb = Encoding.UTF8.GetBytes(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
byte [] hash = sha1.ComputeHash(keyb);
string accept = Convert.ToBase64String(hash);
SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider();
byte [] keyb = Encoding.UTF8.GetBytes(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
byte [] hash = sha1.ComputeHash(keyb);
string accept = Convert.ToBase64String(hash);

5. 開発 - 送受信

クライアント側の送信処理は以下の通りです。

webSocket.send(document.getElementById("input").value + "\r\n");

「WebSocket」オブジェクトのsend関数へ送信したい文字列を設定するのみです。
上記では、ページ内のinputというエディットボックスの内容に改行コードを付加して送信しています。

通信データは特定の通信フレームの形式となります。フレームの詳細は「RFC 6455」を参照してください。今回は、以下の前提で処理しています。

  • FINは必ず「1」(1フレームで完結する)。
  • MASKは必ず「1」(後述します)。
  • RSV1、RSV2、RSV3は無視。
  • Opcodeは必ず「1」(テキストデータ)。
  • Payload lenは「125以内」(Extendex Payload length は使わない)。

各種ヘッダ情報の後ろには実データがあり、Masking-keyでマスクされているため、これを解除し、データを求めます。
処理としては、順次XORを行うのみです。

サーバ側の処理は以下の通りとなります。

int m = 0;
for (int n = 0; n < length; n++)
{
payLoadData[n] ^= maskingkey[m];
m = (m + 1) % 4;
}

チャットなので現在接続されているクライアントすべてに受け取ったデータをそのまま送信します。送信も先ほどと同様のフレーム構成でデータを構築し、データを送信します。
※ただし、MASKは「0」、MaskingKeyも「なし」としました。(「あり」だと正常に動作しなかったため。)

クライアント側の受信処理は以下の通りです。

webSocket.onmessage = function(event)
{
document.getElementById("log").value += event.data;
};

「WebSocket」オブジェクトのonmessageイベントに関数を登録しておくことで、データ受信時に関数が呼び出されます。あとは引数で渡される受信データを処理するのみです。
上記では、ページ内の「log」というエディットボックスへ内容を追記しています。

6. 課題

前述したMASKの件も含めて幾つかクリアしなければならない課題があります。実は「WebSocket」は、2011年の春にも取り上げることを検討し、実際に開発および動作確認まで行っています。しかし、当時は対応しているブラウザが少ないなどの状況もあり、見合わせていました。今回ブラウザの対応状況も大きく違ってきましたので、取り上げることにしたのですが、大きな誤算がありました。
「WebSocket」は策定中の技術であったため前回検討した時と現在の仕様が大きく異なっており、当時のプログラムが全く動きませんでした。今回、とりあえず、動くようにはしましたが、仕様のすべてを盛り込んでいるわけではありません。例えば、以前は、通信データはテキストのみの仕様でしたが、現在はバイナリも扱える仕様になったようです。これ以外にも把握できていない仕様が多くありそうです。今後、これらをひとつひとつクリアにしていきたいと思っています。

7. まとめ

現時点では、まだ多くの課題を残していますが、機器やOS、ブラウザに依存せず、どこからでも対象を制御・監視したいという目的に「WebSocket」は十分使えそうだという手ごたえは得ることができました。
HTML5の普及も進んでおりWebブラウザでも十分にリッチなユーザインターフェースを表現することができるようになってきました。それに加え「WebSocket」で通信した先のサーバで各種、機器を操作するプログラムを組むことで、様々なことが実現可能です。引き続き「WebSocket」への理解を活用していきたいと思います。

(H.T.)

[参考文献]
RFC 6455 -
The WebSocket Protocol (http://tools.ietf.org/html/rfc6455

関連ページへのリンク

関連するソフテックだより

ページTOPへ