Pusherでお手軽WebSocket – iPhoneから加速度をリアルタイム送信 このエントリをはてなブックマークに登録

2011年01月19日

ishizimaishizima / ,

はじめに

タイガーマスクが貢いでくれるのを待っている皆さんこんばんは。
前振り考えるのが面倒なので結論から書くと、今日はWebSocketについて扱おうと思います。iPhoneを使って↓こんなサンプルを作ってみました。

Safari上のJavaScriptと、WebSocketをWebサービスとして提供しているPusherを利用して、サーバサイドの言語を使わずに実装しています。

WebSocketってなぁに?

httpおさらい

Webの通信に使われる皆さんお馴染みのhttpは、クライアント(ブラウザとかね)とサーバが常に「問い合わせ(リクエスト)と返答(レスポンス)」のペアでやりとりをおこないます。

クライアント(FirefoxやChromeなど): 「情報ください」
サーバ(Apacheやnginxなど): 「はい、どうぞ」

このシンプルな仕組みがhttpの強みであり、URLと共にここまで普及した理由と言えるでしょう。

httpの欠点を補うWebSocket

httpはシンプル故に、常にクライアントから問い合わせないとサーバで何が起きているか分かりません。例えばtwitterであれば、TLを更新するのに何秒かに一度TwitterのAPIサーバに問い合わせすることになります。
要するに教えて君です。訊かれる側は大変ですよね。頻繁なサーバへの問い合わせは負荷になる上、リアルタイム性に劣る仕組みであることは想像に難くありません。
そこで、httpの上でサーバとクライアントを糸電話のように繋ぎっぱなしにして効率とリアルタイム性を上げる仕組みが登場しました。それがWebSocketです。
詳しいことは以下の連載が非常によくまとまっているので一読されることをオススメします。Pusherの中の人の記事です。本記事を書く上でも非常に参考になりました。

サーバなしでWebSocketしてみる

本来、WebSocketはサーバとクライアントがセットになっている技術です。冒頭で触れた通り、サーバ側にPusherを利用することで手軽にWebSocketに触れてみたいと思います。

WebSocketを手軽に扱える “Pusher”

まずはPusherのアカウントを作るところから始めましょう。無料プランもあって誰でも手軽に始めることができます。詳しいことは上で挙げた@ITの記事をご参照ください。

Pusher – bringing the realtime web to your apps with HTML5 Websocket

http://pusherapp.com/

このPusherは「データを投げる方はWebサービスAPI経由」、「データを受け取る方はWebSocket経由」というアーキテクチャを採用しています。

Pusher概要

Pusherのサイトより。


後者のデータを受け取る方はJavaScriptのライブラリが提供されており、WebSocketを意識せず簡単にデータを取得することができます。一方前者のRESTfulなAPIはWebサーバからデータを送ることを前提としているため、提供されているライブラリにJavaScriptは含まれていません。(Pusher REST API libraries
といってもhttp経由で実行するWebサービスAPIなので、言語非依存で呼び出すことができます。詳細は以下の公式ドキュメントをご参照ください。
http://pusherapp.com/docs
登録が完了したら、何かしらアプリを作成しましょう。

サンプルを作る

今回はiPhoneの加速度センサーの値をリアルタイムにiPadに送るというのをサンプルのテーマにしました。いや別に送る先はiPadじゃなくてもいいんですけどね。

Pusherへデータを投げる

前述の通り、JavaScript用ライブラリは用意されていないので自前で用意する必要があります。今回はターゲットをiOSのSafariに絞っているのでクロスブラウザ的なところはあまり考えません。

iPhoneで加速度を取得してPusherへ投げる
 ↓ RESTなAPI
PusherからクライアントへWebSocket経由でデータが投げられる
 ↓ WebSocket
iPadで結果を表示

という具合です。

加速度を取得する

取得自体は簡単です。devicemotionにリスナを登録するだけ。
以下のサンプルではイベントハンドラからPusherへデータを投げる関数を呼んでいます。
今回は加速度情報をそのまま送ることにします。

window.addEventListener('devicemotion', function(evt){
  sendAccel(evt.accelerationIncludingGravity);
}, true);

DeviceMotionイベントの詳細は以下のサイトをご参照ください。

DeviceMotionEvent Class Reference

Pusher APIへのアクセス

イベントハンドラから呼ばれた先で、PusherのRESTfulなAPIを呼んでみましょう。
必要なライブラリを以下に示します。

各プラグインのおかげでサンプルを作る事ができました。各作者の方に感謝申し上げます。

function sendAccel(accel){
  if((+new Date) % 100 < 66) return false; // 送信レートを下げる (ざっくり確率を2/3に)
  var body            = $.toJSON(accel);
  var body_md5        = CybozuLabs.MD5.calc(body);
  var auth_timestamp  = ~~(new Date/1000);
  var auth_key        = 'Pusherの管理画面で得られるkey';
  var svr             = 'http://api.pusherapp.com';
  var path            = '/apps/{ここにpusherのapp_id}/channels/{Pusherのチャンネル名}/events';
  var event_name      = 'devicemotion'; //今回はイベント名そのまま
  var string_to_sign  = "POST\n" + path + "\nauth_key=" + auth_key + "&auth_timestamp=" + auth_timestamp + "&auth_version=1.0&body_md5=" + body_md5 + "&name=" + event_name;
  var auth_signature  = '';
  var uri             = '';
  var secret_key      = '管理画面で発行されるsecret';
  auth_signature = HMAC_SHA256_MAC(secret_key, string_to_sign);
  uri =
     svr + path + "?" +
    "auth_key=" + auth_key + "&" +
    "body_md5=" + body_md5 + "&" +
    "auth_timestamp=" + auth_timestamp + "&" +
    "auth_signature=" + auth_signature + "&" +
    "name=" + event_name + "&" +
    "auth_version=1.0"
  ;
  $.post(uri, body, function(data, status) {/* 送出後何かするならここに */});
}

以上のコードで送信側は準備が整いました。devicemotionイベントがPusherにPOSTされます。送りっぱなしでエラー処理など省いていますが、サンプルと言うことでご容赦ください。
お気づきと思いますが、アカウントごとに発行される秘密鍵をJS内に記述する必要があります。従って、あくまでも実験用と捉えてください。このコードを公開された場所に設置するのは避けた方が良いでしょう。また、イベントが発生するがままにデータをPOSTするのはサーバ負荷的にも迷惑がかかる可能性があるため、回数を減らす工夫は必要だと思います。

Pusherからデータを受け取る

次にPusherへ投げられたiPhoneからの加速度情報を受け取ります。
こちらはPusher純正のライブラリが存在するので、楽にWebSocket経由のメッセージを受け取ることができます。
今回はCSSのTransformを使って画像を傾けています。以下のコードは思いっきりWebKit依存ですのでご注意ください。

      // Enable pusher logging - don't include this in production
      Pusher.log = function() {
        if (window.console) window.console.log.apply(window.console, arguments);
      };

      // Flash fallback logging - don't include this in production
      WEB_SOCKET_DEBUG = true;

      var pusher = new Pusher('{pusherの管理画面で発行されるauth_key}');
      var angle = 0;
      pusher.subscribe('{APIにPOSTするときのチャンネル名}');
      pusher.bind('{APIにPOSTするときのイベント名}', function(data) {
        angle =  90 * data.x / 9.8; // 9.8は重力加速度
        if(data.y > 0)  angle = 180 - angle; // 水平より下へ傾いた場合
        document.getElementById('sample_image').style.webkitTransform = 'rotate(' + angle + 'deg)';
      });

上記のコードでidにsample_imageと名前が付けられたDOM要素がiPhoneの傾きと同じような感じになると思います。あんまり厳密じゃないですけどね。

目論見は失敗に……

元々はサーバサイドプログラムなしでPusherを使えば簡単にWebSocketを活用できる!的な記事にしたかったんですけど、JavaScript内にsecretなkeyを書くわけにいかないため実用的じゃない記事になっちゃいました。まぁアカウント情報を送らなければならないことを考えれば仕方ないですよね。
ただ、WebSocketのリアルタイム性、Pusherのお手軽さなんかは伝えられたんじゃないかなと思います。呼び出す側はローカルで動くスクリプト言語でも構わないわけですし。
今後、もっとスマートフォンのような日常に密着したデバイスでもhtml5サポートが充実してきていることもあり、もっともっとリアルタイムWebの楽しさが広がっていくんじゃないかなと思っています。

その他の参考サイト、謝辞

以上、諸々参考にさせていただいた皆様、ありがとうございました。

関連記事

クレイについてもっと知りたい方は…

  1. クレイの3つの強みを見てみる。
  2. WEBシステムのことなら何でもご相談ください。

「いいね!」で応援よろしくお願いします!

このエントリーに対するコメント

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)


トラックバック
  1. Pusherでお手軽WebSocket – iPhoneから加速度をリアルタイム送信 | KRAY Inc | 真実を知ればおのれは自由になる2011/02/21, 12:53 PM

    […] Pusherでお手軽WebSocket – iPhoneから加速度をリアルタイム送信 | KRAY Inc. Tagged with: WebSocket  If you enjoyed this article, please consider sharing it! […]

we use!!Ruby on RailsAmazon Web Services

このページの先頭へ