LocalConnectionとは?

今回は、flash.net.LocalConnectionクラスについての解説を行う。

LocalConnectionクラスとは、AIR以前からも存在した、Flex/Flashアプリケーション同士で通信を行うための仕組みである。同じマシン上で動作するSWFアプリケーション同士で行うことのできる、プロセス間通信だと思ってよい。APIが非常にシンプルな割には、ActionScriptのオブジェクトをそのまま渡す事ができるなど、かなり強力な仕組みだ。

AIRではLocalConnectionはさらにパワーアップしており、AIRアプリケーション同士で通信をできるのみならず、ブラウザ上で動作しているFlexアプリケーションとも通信が可能だ。

では、まずはLocalConnectionの使用法を見ていこう。

LocalConnectionの使用法

LocalConnectionは単純に言えば、文字列で名前を指定した接続を介して、クライアントとサーバが通信を行う仕組みだ。なので、送信側のコードと受信側のコードに分けて見ていくと分かりやすい。

ということで、データを送信する側のコードを先に見ていこう。 送信側のコードは、簡単に書くと次のようになる。

// (1) LocalConnectionのインスタンス作成
var con:LocalConnection = new LocalConnection();

// (2) 送信結果をハンドリングするためのリスナを追加
con.addEventListener(StatusEvent.STATUS,
    function(event:StatusEvent):void {
        switch (event.level) {
            // 正常に送信完了
            case "status":
                break;
            // エラー
            case "error":
                break;
            // 警告
            case "warning":
                break;
        }
    });

// (3) データ送信
con.send("接続名", "受信側のメソッド名", 引数...);

(1) LocalConnectionのインスタンスを作成している。引数なしのコンストラクタを呼び出すだけだ。

(2) 送信がエラー終了した時のために、イベントリスナを追加している。送信結果は、リスナメソッドの引数に渡されるflash.events.StatusEventクラスのlevelプロパティを参照すれば良い。

(3) データの送信には、LocalConnectionクラスのsendメソッドを使用する。sendメソッドの第一引数に渡す「接続名」については後述。第二引数は、受信側でデータを受け取るために用意するメソッドの名前。第三引数以降がデータで、任意のオブジェクトを複数渡すことができる。

では、send()メソッドの第一引数に渡す「接続名」について解説する。「接続名」とは、通信を行うアプリケーション同士で共有する文字列だ(例えば"myConnection"など)。さらに送信する側としては、受信側を特定するための情報を接続名の前に付与する必要がある。 これは以下のルールに則っている。

  • 受信側がFlexアプリケーションの場合は、そのSWFの配布元となったドメイン名を接続名の前に付与する。つまり、接続名は「www.example.com:myConnection」などとなる。間の「:」は、ドメイン名と接続名の間の区切り文字だ。

  • 受信側がAIRアプリケーションの場合は、「app#アプリケーションID」という文字列を接続名の前に付与する。アプリケーションIDとは、アプリケーションディスクリプタファイルのルート要素applicationの、appId属性で指定する任意の文字列だ(アプリケーションディスクリプタファイルについては、連載第3回にて解説を行っている)。例えば、アプリケーションIDが「jp.co.mycom.journal.App」だった場合、接続名は「app#jp.co.mycom.journal.App:myConnection」などとなる。

では次に、受信側のコードを見ていこう。LocalConnection経由でデータを受信する側のコードは以下のようなものになる。

// LocalConnectionのインスタンス作成
var con:LocalConnection = new LocalConnection();

// (1) 接続を許可するドメイン/アプリケーションIDを指定
con.allowDomain("jp.co.mycom.journal.App");

// (2) データの受信に失敗したイベントのリスナを登録
con.addEventListener(AsyncErrorEvent.ASYNC_ERROR, onAsyncError);

// (3) セキュリティ上許可していないドメイン/アプリから接続されたイベントのリスナを登録
con.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError);

// (4) データを受け取るメソッドを持つインスタンスを、
//     `client`プロパティに指定
con.client = this;

// (5) 接続名を指定してconnectメソッドを呼び出す。
con.connect("接続名");


...

// (6) データ受信用のメソッド
function onReceived(msg:String):void {
    ...
}

ポイントを解説していこう。

(1) LocalConnectionのインスタンスを作成した後、(4)でconnect()メソッドを呼び出す前に、allowDomain()メソッドを使用して接続を許可するクライアントのドメイン、もしくはアプリケーションIDを指定する必要がある。受信側がFlexアプリケーションの場合は必須。AIRアプリケーションの接続では、これを行う必要はない。「*」(アスタリスク)を使用すると、接続名さえ正しければあらゆる接続を受け入れる。

(2) クライアントが送ってきたデータを受信するのに失敗した(受信用のハンドラメソッドが存在しない、など)場合に発生するエラーを処理するためのリスナメソッドを登録している。

(3) セキュリティ上許可していないドメイン/アプリケーションから接続された際に発生するエラーを処理するためのリスナメソッドを登録している。

(4) 送信されたデータを受け取るメソッド(そのメソッド名は送信側のsend()メソッドで指定される)を持つインスタンスを、LocalConnectionclientプロパティに代入する。データが到着した際、そのメソッドがランタイムによって呼び出される。

(5) 接続名を指定してconnect()メソッドを呼び出す。受信側は、送信側と違って接続名だけを使用する。

(6) 送信側が送ったデータを引数に取るメソッドを用意しておく。メソッド引数の数と型は、送信側がsend()メソッドを呼び出す時に指定した引数と一致する必要がある。

以上で、LocalConnectionを使用したコードの説明は終わりだ。では、今回のサンプルアプリケーションの解説に入ろう。

サンプルアプリケーションの解説

今回用意したサンプルは、データの送信側と受信側に分かれている。

送信側のテキストエリアに文字を入力すると、ローカルコネクション経由で文字列が送られ、受信側のテキストエリアに反映される。

テキストエリアに入力した文字列が自動で同期する

また、受信側のコードを変えることなくFlexアプリケーションにしたとしても、送信側の接続文字列を変えるだけで同じように動作する。

受信側をFlexにしても、ほとんど修正することなく動作する

まず、送信側のソースコードは以下の通り。

リスト: 送信側のAIRアプリケーション「AIRLocalConnectionExample.mxml」

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="init()">
    <mx:Script>
        <![CDATA[
            import flash.net.LocalConnection;

            // LocalConnection.send()メソッドに渡す接続名
            //   受信側がローカルホスト上で動作するFlexアプリケーションの場合
            private static const CONNECTION_NAME:String = "localhost:myConnection";
            //   受信側がAIRアプリケーションの場合
            //private static const CONNECTION_NAME:String = "app#AIRLocalConnectionReceiver:myConnection";

            // ローカルコネクション
            private var con:LocalConnection = new LocalConnection();

            // creationCompleteで呼び出される初期化メソッド
            private function init():void {
                // 送信結果をハンドリングするためのリスナを追加
                con.addEventListener(StatusEvent.STATUS, onSend);
            }
            // テキストエリアの状態が変更されたら呼び出されるメソッド
            private function onTextChanged():void {
                con.send(CONNECTION_NAME, "onTextReceived", textInput.text);
            }
            // send()メソッドの結果を処理するリスナメソッド
            private function onSend(event:StatusEvent):void {
                if (event.level == "error") {
                    trace("Send Error");
                }
            }
        ]]>
    </mx:Script>
    <mx:TextArea id="textInput" x="10" y="28" width="453" height="318" change="onTextChanged()"/>
    <mx:Label x="10" y="2" text="ローカルコネクションのサンプル"/>
</mx:WindowedApplication>

次に示すのが受信側のコードだ。

リスト: 受信側のFlexアプリケーション「AIRLocalConnectionReceiver.mxml」

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="init()">
    <mx:Script>
        <![CDATA[
            import flash.net.LocalConnection;

            // ローカルコネクション
            private var con:LocalConnection = new LocalConnection();

            // creationCompleteで呼び出される初期化メソッド
            private function init():void {
                // 接続を許可するドメイン/アプリケーションIDを指定
                con.allowDomain("*");
                // データの受信に失敗したイベントのリスナを登録
                con.addEventListener(AsyncErrorEvent.ASYNC_ERROR, function(event:AsyncErrorEvent):void {
                    textOutput.text += (event.text + "\n");
                });
                // セキュリティ上許可していないドメイン/アプリから接続されたイベントのリスナを登録
                con.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function(event:SecurityErrorEvent):void {
                    textOutput.text += (event.text + "\n");
                });
                try {
                    // データを受け取るメソッドを持つインスタンスを、clientプロパティに指定
                    con.client = this;
                    // 接続名を指定してconnectメソッドを呼び出す
                    con.connect("myConnection");
                } catch (e:ArgumentError) {
                    trace("接続名が既に使用されています。");
                }
            }
            // データ受信用のメソッド
            public function onTextReceived(msg:String):void {
                textOutput.text = msg;
            }
        ]]>
    </mx:Script>
    <mx:TextArea id="textOutput" x="10" y="28" width="453" height="318" editable="false"/>
    <mx:Label x="10" y="2" text="LocalConnection経由で受けとったテキストが下に表示されます。"/>   
</mx:Application>

今回は、ローカルコネクションに関する説明は既に詳しく行ったため、ソースコード中のコメントをもって解説と代えさせていただきたい。