 |
 |
|
 |
チュートリアル
- XMLSocketで何ができるのか
XMLSocketは、Flashファイルと他のマシンとの間に、双方向の接続を確立し、XMLデータをやりとりするためのActionScriptのオブジェクトです。他のマシン(もしくはプログラム)との間に継続的な接続が必要な場合に使うもので、とくにマルチユーザコンテンツを製作するためだけにあるというわけではありませんが、目的として誰もが考えるのはやはりマルチユーザであろうということで、このサイトではFlashによるマルチユーザコンテンツの製作を扱います。
まず、わかりやすい例として、chatが挙げられます。chatといってもCGIを使っ たWeb chatの代替物ではなく、接続状態を保ったままの、IRCのようないわゆる普通のchatです。もちろんチャットだけではなくいろいろ可能性はあるのですが、 とりあえずわかりやすいのでchatを例にして説明します。まず、「マルチユーザ」という言葉を説明なしにいきなり使ってしまっていますが、ここでは、別々 のマシンを使っている複数のユーザ同士が、リアルタイムにアプリケーション 上の状態、情報を共有していることを指します。どうやって共有させるかというと、ここでXMLSocketがでてきます。まずFlashファイル(swfファイル)を置 いてあるWebサーバと同じマシン上にサーバプログラムを走らせる必要があります。サーバプログラムは、既に開発されているものもありますが、このペー ジでは独自開発のプログラムを用います。サーバプログラムのプログラミング 言語にはJavaを使用していますが、Javaの知識はここでは特に必要ありません(もちろんあればそれに越したことはありませんが)。 ブラウザにロードされたswfファイルはswfファイルのActionScript(のXMLSocketオブジェクト)内で指定されたサーバに接続を試み、接続が確立したあとは接続状態をキープし続けます。
chatであれば、ある一つのクライアントがメッセージをサーバに送ると、サーバはそのメッセージを全てのクライアントに配らなければなりません。 つまりリアルタイムに情報を共有するには、あるクライアントがサーバに 送った情報が即座に他の全てのクライアントに配分される必要があるのです。 Flash4まではFlashのActionScriptの機能の中に他のマシンとの接続を確立しキープする機能(ソケット)がなかったので、このようなことができませんでした。 このXMLSocketの機能を使えば、ネットワークゲームのようにネット上の 仮想空間をFlashで構築することも可能です。 とりあえず、XMLSocketを使ったサンプルを見てみたい、という方は、 この下についているFlashバナーをご覧ください。
あなたのインターネット環境で使われているファイヤウォールやルータの設定によっては、「サーバに接続できません」というエラーメッセージが出てしまうかもしれません(もしかしたらウチ(faces.bascule.co.jp)のサーバが落ちてるだけかも。。。そのときはごめんなさい!)が、あきらめることはありません。あとでサーバも手元のマシン(もしくは同じLAN内のマシン)でサーバを動かす方法を説明します。バナーのどこか一箇所をクリックすると、コックさんがでてきて、マウスでクリックされた場所に向かってトコトコ歩いていく姿が見られれば、サーバへの接続に成功しています。これは実は、クリックされた場所の情報をマルチユーザサーバ(faces.bascule.co.jp)に送って、サーバから送り返されてきた情報に基づいてコックさんを移動させているのです。この場合、何もソケットを使ってサーバに情報を送り付けなくても、マウスでクリックした位置に単に移動させればいいじゃないか、と思うかも知れません。そこで、マルチユーザ状態にするために、そばに空きマシンがもう一台ある方はそちらでも同じページにアクセスしてみてください。他にマシンがない方は、もう一つブラウザウィンドウを立ち上げて同じページ(このページ)にアクセスしてみてください。そうすると、もう一人コックさんがでてきます。どちらか片方のバナーでコックさんを移動させると、もう一つのバナーでもコックさんが同じように移動します。要するにこれがマルチユーザです。これがXMLSocketがでてくるまでFlashではできなかった。ネットゲームにはまっているような人には、「当たり前のことをなにクドクドいってるんだ」といわれそうですが、Flashでこれ(マルチユーザ)ができるとネットゲームを含むネットコンテンツ製作の裾野がひろがると思いませんか?
目次に戻る
- サーバプログラムをセットアップする
では、Flashでこういうマルチユーザコンテンツを自分で作るにはどうしたらよいでしょうか。まず必要なのは、当たり前ですが、Macromedia Flash5です。XMLSocketはFlash5以降の機能なのでFlash4以前には含まれていません。次に必要なのが、サーバプログラム(とそれを動かすマシン環境)です。Flash用のサーバプログラムは、商用、フリー含めていくつか種類がありますが(リンク集)、ここではこのサイト(FACEs)オリジナルのFACEs Serverを使って説明します。FACEs ServerはJavaで書かれていますが、とりあえずJavaやプログラミングの知識は必要ありません。FACEs Serverは100% Pure Javaで書かれており、JRE(Java Runtime Environment)1.3以上が動く環境が必要です。現状で動作を確認しているOSはMicrosoft Windows(2000,98,95,NT4.0), Linux(RedHat6.2, RedHat7.0.1J, RedHat7.1), Solaris2.6です。FACEs Serverの開発、運営はLinuxで行っていますが、ここを読んでいる方の手元にある可能性の高いOSとしてとりあえずWindowsを例にとって手順を説明します。あなたが普段Windows版のFlash5を使用しているのであれば、その同じマシンで結構です。
- まずダウンロード
ダウンロードページにログインし、zipパッケージ(faces0_1_7.zip)をダウンロードしてください。
- Java2 SDKのインストール
実は、FACEsサーバを動かすだけならJava2 SDK(Software Development Kit)は必要ないのですが、JREだけインストールしてあとでSDKが必要になると二度手間ですから、思い切ってインストールしてしまいましょう。30MB近いパッケージをダウンロードするのは厳しい、という人は、Java関連雑誌のCD-ROMを利用することもできます。
- まずインストールパッケージをSunのサイトからダウンロード
- 展開、インストールします。詳細はJava(TM)2 SDK Standard Edition インストールガイドにあります。
- PATH変数を更新します。上記のインストールガイドに詳細が書いてありますが、以下Windows2000の場合を示します。
- [スタート]から[設定]、[コントロールパネル]、[システム]の順に選択し、[詳細]タブを選択します。
- [ユーザ環境変数]と[システム環境変数]で"PATH"を探します。
- "PATH"の最初(先頭)にパスを追加します。パスは、JDKをインストールした場所が例えばC:\j2sdk1.4.0であれば、
C:\j2sdk1.4.0\bin
になります。
- [ユーザ環境変数]と[システム環境変数]で"CLASSPATH"を探します。
- カレントディレクトリ(".")にCLASSPATHが通っていなければ、カレントディレクトリ(".")を追加します。
例えば、CLASSPATHの変数値が、
c:\windows\java\classes;
となっていたら、カレントディレクトリにパスが通っていないので、
c:\windows\java\classes;.
というように、最後に"."を追加します。
- JAXPのインストール(J2SEのバージョン1.4以上をインストールした方はJAXPのインストールの必要はありません)
JAXP(Java API for XML Processing)をインストールします。JAXPはJava XML Packに含まれています。以下手順です。
- zipアーカイブ(java_xml_pack-spring02-dev.zip)をダウンロード
- Winzip等で解凍、展開する。
- <展開したフォルダ>\java_xml_pack-spring02-dev\jaxp-1.2-ea2フォルダを開く。
- 中にある.jarファイル全てをを<Java2 SDKをインストールしたフォルダ>\jre\lib\extフォルダにコピーする。Java2 SDKをインストールしたフォルダがC:\jdk1.3.1_02であれば、
C:\jdk1.3.1_02\jre\lib\ext
に.jarファイル全てをコピーします。
- FACEs Serverの起動
- 最初にダウンロードしたfaces0_1_7.zipを解凍、展開します(facesという名前のフォルダができます)。
- コマンドプロンプトを起動します。Windows2000であれば、[スタート]から[プログラム]、[アクセサリ]、[コマンド プロンプト]の順に選択すれば起動します。
- 解凍してできたfacesというフォルダに移動します。展開した場所がC:\であれば、
C:\>cd faces
などとすれば移動できます。
- FACEs Serverを起動します。コマンドプロンプトで
C:\faces>java jp.faces.FacesServer 8080
のようにすれば起動できます。最後の数字は、FACEsサーバが指定するポート番号で、1024以上の数字を指定してください。
[2002/05/20 13:27:37] starting server...
[2002/05/20 13:27:37] server started on port 8080
のように表示されれば、起動は成功です。
ここで、
[2001/05/20 13:27:37] Exception in thread "main"
java.lang.NoClassDefFoundError: jp/faces/FacesServer
などのように表示されて起動できない場合は、もう一度、CLASSPATHとPATHの設定を確認してください。
目次に戻る
- サーバパッケージ付属サンプルのFlashファイルをサーバに接続させてみる
- faces\sampleにあるtest.flaをFlash5でオープンします。
- ムービエクスプローラを開きます。
- [actions for frame1]とある所をダブルクリックしてフレームアクションを開きます。
- 上から4行目の
mySocket.connect("localhost", 8080);
とあるところに注目しましょう。
もし、サーバプログラムを起動したマシンが、Flash5を起動しているマシンと同一のマシンで、かつポート番号を8080で起動してあるならば、何も変更する必要はありません。もし、サーバプログラムを起動したマシンがLAN内の他のマシンであれば、localhostとなっているのところをそのマシンのマシン名かローカルIPアドレスに置き換えてください。たとえば
mySocket.connect("10.0.0.2", 8080);
のように修正します。
- [ファイル]、[ムービーの書き出し]を選択して、test.swfというファイルを生成します。
- test.swfを2つ起動して、マウスの動きに白い円がくっついてきて、二つのウィンドウで動きが共有されていれば接続成功です。
目次に戻る
- サーバパッケージ付属サンプルの中身はどうなっているのか
このtest.flaは、とりあえず手元で立ち上げたサーバプログラムが動作しているかどうかを調べるための、ほとんど最小限の機能しかもたないFlashサンプルですが、同時にXMLSocketを用いてマルチユーザFlashコンテンツを作るための本質的な手法を含んでいるので、XMLSocketの説明と併せてtest.flaのActionScriptの中身をざっと解説します(もうちょっと具体的なコンテンツサンプルはまたあとで説明します)
まずActionScriptのXMLSocketオブジェクトの特徴について、Flash5付属のActionScript辞書から引用します。
------------------------------------------------------------------------
- XMLメッセージは、全二重TCP/IPストリームソケット接続を介して送られます。
- 各XMLメッセージは、ゼロバイトで終了された完全なXMLドキュメントです。
- 無数のXMLメッセージは、1つのXMLSocket接続を介して送受信することができます。
------------------------------------------------------------------------
1の言い回しはなんだか妙に大げさですが、全二重というのは送信と受信を同時に行うことができるということで、TCP/IPはインターネットの標準プロトコルで、ソケットにはストリームソケットとデータグラムソケットがあって、、、というような説明はまた別の機会にするとして、要するにXMLSocketはTCPというプロトコルをつかって双方向通信を行うということです。
2は、XMLSocketオブジェクトでやりとりするメッセージはXMLドキュメントであるということです。実はXMLでない普通のテキストも送受信できますが、ここでは詳しく触れません(詳しく知りたい方は検索エンジンでXMLSocket onDataのキーワードで調べてみるとわかるかも知れません。)。Flash5でXMLドキュメントを扱うXMLオブジェクトについてはまた別に説明したいと思いますが、詳しく知りたい方はWEBのお仕事あたりが分かりやすそうです。ゼロバイトというのは、サーバプログラム(Java)上で"\0"というように表記されていますが、ActionScriptを書く上では意識する必要はありません(送信時にはFlashが自動的に付加する)。
3は、要するに一つXMLSocketオブジェクトのインスタンスを生成しておけば、サーバとの接続にはずっとそのXMLSocketを使い回すことができ、メッセージごとに生成する必要はないということです。
このXMLSocketオブジェクトには以下のメソッドがあります。
以下の表もActionScript辞書からの引用です。
メソッド |
説明 |
close
|
開いているソケット接続を閉じます。 |
connect
|
指定されたサーバーへの接続を確立します。 |
onClose
|
XMLSocketの接続が閉じられたときに呼び出される関数。 |
onConnect
|
XMLSocketの接続が確立されたときに呼び出される関数。 |
onXML
|
XMLオブジェクトがサーバーから着信したときに呼び出される関数。 |
send
|
XMLオブジェクトをサーバーに送ります。 |
「〜たときに呼び出される関数」というのは、コールバック関数とよばれ、プログラムから直接呼び出すのではなく、なにか特定のイベントが起きたときに呼び出されるように指定するものです。これらの6つのメソッドは全てtest.flaの中にでてくるので、そのつど説明します(ちょっと触れたように、Flash5において正式に仕様には書かれていないonDataというメソッドがありますが、ちゃんとテストしていないので別の機会に触れます。)。もういちどtest.flaを開きましょう。
- faces\sampleにあるtest.flaをFlash5でオープンします。
- ムービエクスプローラを開きます。
- [actions for frame1]とある所をダブルクリックしてフレームアクションを開きます。
function xxxxx(){
...........
}
というかたちで関数の定義が最初から最後までずらっと並んでいます。これらの関数はすべて他のムービクリップオブジェクトのインスタンスやボタンから呼び出すために用意したものです。上から順にそれぞれの関数を説明します。説明の便宜のために、各行の頭にその関数内での行番号をつけています(実際のスクリプトには行番号は入っていません)。
- loadEnd関数
サーバへの接続時に呼ぶ関数です。このサンプルではloadactionというインスタンスがロードされた瞬間に呼ばれます。
1: function loadEnd () {
2: mySocket = new XMLSocket();
3: mySocket.onConnect = checkConnect;
4: mySocket.connect("localhost", 8080);
5: mySocket.onClose = checkClose;
6: }
2行目でXMLSocketオブジェクトのコンストラクタを呼び出し、XMLSocketオブジェクトを作成しています。
3行目でサーバとの接続が確立されたときに呼ばれるコールバック関数(ここではcheckConnect)を設定しています。
4行目はconnectメソッドの1つめの引数で接続先のサーバの名前、2つめの引数でポート番号を指定しています。
1つめの引数はここではlocalhostになっていますが、これはサーバとクライアントが同じマシンにある場合にのみ有効な記述です。普通にWeb上でクライアントを公開する場合は、FACEsサーバプログラムの走っているマシンの名前をいれます。サーバプログラムは、クライアントになるSWFファイルの置いてあるWWWサーバと同じマシンか、もしくは同じサブドメイン内のマシン上で動いていないと接続できません。これはFlashセキュリティ上の制限です。例えば、FACEsサーバプログラムがwww.faces.jpというマシンの8080番ポートで走っているなら、
mySocket.connect("www.faces.jp", 8080);
という記述になります。この場合、クライアントのSWFファイルはwww.faces.jp, もしくはfaces.jpドメインに属するマシン(例: www2.faces.jp, hogehoge.faces.jpなど)に置かれていなければ、サーバに接続できないということになります。
5行目でサーバから接続が閉じられたときに呼ばれるコールバック関数(ここではcheckClose)を設定しています。
- checkClose関数
サーバから接続が閉じられたときに呼ばれるコールバック関数です。デバッグ用に文字列を出力する以外は何もしていません。
1: function checkClose () {
2: trace ("disconnected");
3: }
- closeSocket関数
サーバとの接続を切る関数です
1: function closeSocket () {
2: mySocket.close();
3: trace ("closeSocket test\n");
4: }
2行目でXMLSocketオブジェクトのcloseメソッドを呼び出してサーバとの接続を閉じています。
- checkConnect関数
サーバとの接続が確立するときに呼ばれるコールバック関数です。引数には、ソケットが正常に確立されたかどうかを示すブール値(trueかfalse)が入ります。
1: function checkConnect (bOK) {
2: if (bOK) {
3: mySocket.onXML = getData;
4: trace ("trueconnection");
5: sendStr("<QN app=\"test\"/>");
6: }
7: else {
8: trace ("falseconnection");
9: }
10: }
if〜else文で接続が確立された場合(bOKがtrue)とそうでない場合(bOKがfalse)に分岐しています。接続が確立された場合にXMLドキュメントの文字列がサーバから着信したときに呼び出される関数を設定し(3行目)、サーバにsendStr関数(下で説明します)でXMLオブジェクトを送っています(5行目)。QNという名前のタグは、FACEs Serverに対して自分(クライアント)固有の番号をリクエストするためにFACEs Server用に定義されています。appという属性で指定されたパラメータ(ここでは"test")は、コンテンツの名称(複数種類のマルチユーザコンテンツが一つのサーバに接続する場合にコンテンツを識別するためにある)を指定しています。このリクエストに対してサーバはNという名前のタグのついたノードを返すことによってクライアント番号を発行します。このようなFACEs Server固有のXMLの取り決めは別にまとめて説明します。
- getData関数
一つのXMLオブジェクトを構成する文字列が着信するたびに、呼び出される関数です。サーバから送られてくるXMLデータを解釈し、内容に対応した処理を行っています。引数には、受信したXMLオブジェクトが入ります。
1: function getData (receiveXML) {
2: var e = receiveXML.firstChild;
3: if (e != null && e.nodeName == "P") {
4: if (e.attributes.n == "1") {
5: setProperty ("_root.mouse1", _x, e.attributes.x);
6: setProperty ("_root.mouse1", _y, e.attributes.y);
7: }
8: else if (e.attributes.n == "2") {
9: setProperty ("_root.mouse2", _x, e.attributes.x);
10: setProperty ("_root.mouse2", _y, e.attributes.y);
11: }
12: }
13: else if (e != null && e.nodeName == "N") {
14: selfname = e.attributes.n;
15: trace ("selfname="+selfname+"\n");
16: }
17: else if (e != null && e.nodeName == "D") {
18: disconnectedname = e.attributes.n;
19: if (e.attributes.n == "1") {
20: setProperty ("_root.mouse1", _x, 0);
21: setProperty ("_root.mouse1", _y, 0);
22: }
23: else if (e.attributes.n == "2") {
24: setProperty ("_root.mouse2", _x, 0);
25: setProperty ("_root.mouse2", _y, 0);
26: }
27: trace ("disconnectedname="+disconnectedname+"\n");
28: }
29: }
2行目で受信したXMLオブジェクトのルート要素を変数eに取り出します。
3行目、13行目、17行目は、ルート要素の要素名に応じて処理を分岐するため
にXMLオブジェクトのnodeNameプロパティを読みだして判定を行っています。3
行目なら、「変数e(ルート要素の入ったXMLオブジェクト)がnull(空)でなく且
つ要素名が"P"だったら」ということです。PというXML要素名は
FACEs Server用に定義された要素名ではなく、このサンプルでマウスポインタ
の位置をやりとりするために定義した要素です。NとDはFACEs Serverで定義さ
れており、NはQN要素をサーバに送信したときの返信としてサーバから帰って
くる要素、Dは他のクライアントがサーバとの接続を断ったときにサーバから送られてくる要素の名前です。
4行目と8行目の、e.attributes.nというのはXMLオブジェクト(eという変数)の、全ての属性のコレクション(attributes)の中の、nという属性で指定された文字列と言う意味です。P要素のnという属性はこのサンプルではこの要素を送信したクライアントの固有の番号を指定しているので、n属性が1か2かで分岐しているということは、このサンプルはサーバを最初から2人で独占してそれぞれ1番と2番をサーバから振られている状態を前提にしていることが分かります(サーバは他のクライアントに使われていない最も小さな番号をクライアントに振るようになっています。通常クライアントは自分に与えられる番号を予測できないので、これはサーバ立ち上げ時のテスト用に特殊な状況を想定していると思ってください)。
5,6行目のsetProperty関数(ActionScriptで元々定義されている)で、mouse1インスタンスのx座標とy座標を指定しています。
8,9行目のsetProperty関数で、mouse2インスタンスのx座標とy座標を指定しています。
13行目から16行目はN要素のn属性で自分(クライアント)の固有の番号を振られた場合の処理を記述しています。n属性の数値をselfnameという変数に格納しています。
17行目から28行目は自分以外の他のクライアントがサーバとの接続を絶ったときにサーバから送られてくるD要素を受信したときの処理を記述しています。ここでは接続を断ったクライアントのマウスポインタの位置に対応するインスタンス(mouse1かmouse2)の位置をx、y座標ともに0にしています。ちなみにこのサンプルの中に数箇所出てくるtrace関数は、単にデバッグや動作追跡の便利のために挟んであるだけなので、動作には直接関係ありません。
- sendPos関数
マウスが動くときに呼び出している関数です。1番目の引数は自分(クライアント)固有の番号、2番目はx座標値、3番目はy座標値です。
1: function sendPos (name, x, y) {
2: str = "<P n=\""+name+"\" x=\""+x+"\" y=\""+y+"\"/>";
3: sendStr(str);
4: }
2行目で
- sendStr関数
引数で指定されたXMLドキュメント文字列をXMLオブジェクトに変換して、XMLSocketのsendメソッドでサーバに送信します。
1: function sendStr (str) {
2: theXML = new XML();
3: theXML.parseXML(str);
4: mySocket.send(theXML);
5: }
2行目ではXMLオブジェクトのコンストラクタを呼び出し、XMLオブジェクトを作成しています。
3行目では、XMLオブジェクトのparseXMLメソッドでXML文字列を解析し、XMLオブジェクトにXMLツリーを設定します。
4行目では、XMLSocketオブジェクトのsendメソッドで、作成したXMLオブジェクトをサーバに送信します。
以上がこのサンプルで定義されている関数の説明です。
次に、マウスにくっついて動くムービクリップのインスタンス用のActionScriptについて見てみます。
ムービエクスプローラで[action_mouse, (mouse1)]の[actions for action mouse]とあるところをダブルクリックしてオブジェクトアクションを開きます。
1: onClipEvent (mouseMove) {
2: if (_root.selfname == "1") {
3: _root.sendPos("1", _root._xmouse, _root._ymouse);
4: }
5: }
マウスが移動したときに、マウスのx,y座標を、frame1で定義したsendPos関数で送信します。自分のクライアント固有の番号が1のときにその番号とx座標とy座標の値を引数に取ってP要素を構築してサーバに送信します。これが自分も含めた全て(といってもこの場合最大2つですが)のクライアントに配られて解釈され、白丸オブジェクトの動きが共有されることになります。[action_mouse, (mouse1)]の方も同様で、自分の番号が2の場合のための記述です。
次は、ロード時にloadEnd関数を実行するためのダミーインスタンス用のActionScriptを見てみます。
ムービエクスプローラで[action_mouse, (loadaction)]の[actions for action mouse]とあるところをダブルクリックしてオブジェクトアクションを開きます。
1: onClipEvent (load) {
2: _root.loadEnd();
3: }
このインスタンスがロードされたときに、frame1で定義したloadEnd関数を呼び出し、サーバへの接続を行います。
最後にボタンのActionScriptを見てみます。
黄色地に黒の"d"ボタンをクリックしたときに、サーバとの接続を切断するようにしています。
1: on (release) {
2: textBox = "disconnected";
3: this.closeSocket();
4: }
3行目でframe1で定義したcloseSocket関数を呼び出し、サーバとの接続を切っています。
目次に戻る
まだまだ説明の足りないところ(特にXMLオブジェクトについてとFACEsサーバ固有のの取り決め)や用語の不適切なところがあるかと思いますので、この文書は随時アップデートしていきます。またもっと具体的なコンテンツの作例も紹介していきたいと思います。
- サンプル1(簡単な作例)
- サンプル2(チャット)
|
|
 |
 |
 |