AS3SX入門2。
やること
セッションとはソケット通信におけるクライアントとサーバーの接続の開始から終了までのことを指す。
クライアント1つの接続につき1つ、Sessionオブジェクトが作られ、クライアントが切断するまでこのオブジェクトが保持される。
Sessionオブジェクトが作成されるのは、どうやらそのクライアントがサーバーに初めてリクエストを送った時のようだ。
このSessionオブジェクトによってクライアントを識別したり、クライアントごとにデータを保存することができる。
ServerAS3SX.sessionプロパティで、SessionManagerクラスオブジェクトを取得できる。このオブジェクトでSessionの管理をする。
SessionManager
サーバーサイドの処理は、コンストラクタ以外は基本的にクライアントのリクエストに反応して動く。
なので処理中には"現在の処理の流れを実行する原因となったクライアント"が存在する。リクエストを送ってきたクライアントだ。
そのクライアントのセッションはgetSessionで得ることができる。返ってくるSessionオブジェクトにはクライアントの情報が入っている。
クライアントから最初のリクエストが来た時にそのSesssionオブジェクトは作られる。
その時に呼び出すハンドラメソッドをaddSessionCreateHandlerで登録することができる。リクエストに対するハンドラメソッドよりも先に実行されるので、クライアントが最初に接続したときに行いたいクライアントごとの初期設定などはここでメソッドを登録するといいだろう。
メソッドの引数には作成されたSessionオブジェクトを受け取る。
クライアントとの接続が切断されたり、クライアントがウィンドウを閉じたりするとSessionオブジェクトは破棄される。
この時に呼び出すハンドラメソッドをaddSessionDisposeHandlerで登録することができる。切断されたときの処理や、クライアント情報の保存などはここでメソッドを登録する。
メソッドの引数には破棄されたSessionオブジェクトを受け取る。
(*注)と思っているのだが、このaddSessionDisposeHandler、実に怪しい挙動をする。
ウィンドウを閉じたときに登録しておいたメソッドが呼ばれたり呼ばれなかったり、#1009エラーが出たり出なかったりと、使い物にならない。
正直原因がまったくわからない。使い方が間違ってるかもしれないので、とりあえず使わないほうがいいだろう。
Session
userIdでクライアントを識別することができる。ClientAS3SXで得られるuserIdと同じGUIDなので利用することがあるだろう。
ちなみにServerAS3SX.sendResponseの第二引数がnullだと、このServerAS3SX.session.getSession().userIdの参照元と同じ値が内部で使われる。なのでデフォルトだと現在のセッションにレスポンスを送る仕組みになっているわけだ。
get/setValueでSessionオブジェクトが内部でもつArrayにキーを指定して値を読み書きすることができる。
内部では、
storage[key] = value;
return storage[key]
のようになっていて、存在しないキーを指定してgetValueするとundefinedが返ってくる。
Arrayなので返り値は型指定されておらず、キャストなりなんなりする必要がある。
この2つのメソッドを使うことで、クライアントごとに個別に値を保存することができる。自分でGUIDでインデックスを作って配列を管理してもいいが、面倒なので用意されているこちらを使うほうが楽だろう。
さきほどのSessinManager.addSessionCreateHandlerと組み合わせてクライアントごとに初期処理をし、値を保存してもいいかもしれない。
では実際にSessionを利用したプロジェクトを作ってみよう。前回のものをちょことだけ改造する。
今回作るのはこれ。
ボタンを押すとサーバーからそのクライアントからのリクエスト回数とサーバーサイドswfが起動してから受けた総リクエスト回数が返ってくる。ボタンの下の文字列はクライアントのGUIDだ。
ウィンドウを複数起動して実験してみると挙動が分かりやすいだろう。
package { /** * ... * @author */ public class Response extends Object { public var totalAccess:uint; public var sessionAccess:uint; public function Response(){ } } }
とくに説明することのないレスポンスオブジェクト。送るべき情報としてサーバーサイドのトータルリクエスト受付回数と、セッションごとのリクエスト回数があるので、それらをクラスのpublic変数にしたもの。
このようにカスタムクラスに入れて送るのが一般的か。
package { import com.as3sx.connection.SocketConnection; import com.bit101.components.PushButton; import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; import flash.text.TextField; import flash.text.TextFieldAutoSize; /** * ... * @author */ public class ClientMain extends Sprite { /// change endpoint private static const END_POINT:String = "http://as3sx.com/Apps/ふにゃんふにゃん/"; private var tf:TextField; public function ClientMain():void { if (stage) init(); else addEventListener(Event.ADDED_TO_STAGE, init); } private function init(e:Event = null):void { removeEventListener(Event.ADDED_TO_STAGE, init); // entry point ClientAS3SX.setEndPoint(END_POINT); addEventListener(Event.ENTER_FRAME, connectionCheck); } private function connectionCheck(e:Event):void { if (SocketConnection.instance.connected){ removeEventListener(Event.ENTER_FRAME, connectionCheck); // connected new PushButton(this, 0, 0, "Request", onPush); var tfID:TextField = new TextField(); tfID.autoSize = TextFieldAutoSize.LEFT; tfID.y = 40; tfID.text = ClientAS3SX.userId; addChild(tfID); tf = new TextField(); tf.autoSize = TextFieldAutoSize.LEFT; tf.y = 60; addChild(tf); ClientAS3SX.setResponseHandler(Response, onResponse); } } private function onPush(e:MouseEvent):void { ClientAS3SX.sendRequest(true); } private function onResponse(response:Response):void { tf.text = "yousr access : " + response.sessionAccess + " total access : " + response.totalAccess; } } }
まず必須の処理としてinitメソッドでsetEndPointをしている。この値END_POINTは人(とプロジェクト)によって違うものなので自分で確認して入れよう。
その後connectionCheckで接続が確認されたら(馬鹿全さんの記事参照)ボタンとテキストフィールドを作る。ボタンの下にはクライアントサイドで得たGUIDを表示している。これはあとでView Logで確認するためのものだ。
ボタンのハンドラにはsendRequestを書いてある。今回特に送りたい情報はなく、ただ単にサーバーにレスポンスの要求を伝えるだけなので、適当にBoolean型のtrueを送ってみよう。
これでボタンを押すたびにtrueがリクエストとしてサーバーに送られる。
同時に、サーバーのレスポンスに備えてsetResponseHandlerをしておく。今回レスポンスとして返ってくるのはesponseオブジェクトなのでResponseクラスを登録する。
ハンドラメソッドではResponseオブジェクトからuintを二つ取り出し、文字列にしてテキストフィールドを更新する。
package { import com.as3sx.log.ServerTrace; import com.as3sx.session.Session; import flash.display.Sprite; /** * ... * @author */ public class ServerMain extends Sprite { private var totalAccess:uint; private var response:Response; public function ServerMain():void { totalAccess = 0; response = new Response(); ServerAS3SX.setRequestHandler(Boolean, onBoolean); ServerAS3SX.session.addSessionCreateHandler(onSessionCreate); } private function onSessionCreate(session:Session):void { ServerTrace.trace(" connected : " + session.userId); } private function onBoolean(bool:Boolean):void { var session:Session = ServerAS3SX.session.getSession(); ServerTrace.trace(" receieve request : " + session.userId); var sessionAccess:uint; if (session.getValue("access") == undefined){ sessionAccess = 1; } else { sessionAccess = session.getValue("access"); sessionAccess++; } session.setValue("access", sessionAccess); totalAccess++; // response.sessionAccess = sessionAccess; response.totalAccess = totalAccess; ServerAS3SX.sendResponse(response); } } }
サーバーサイドではコンストラクタでまずtotalAccessを0にしている。この処理はサーバーサイドswfが起動されるたびに0にリセットされる。また、Responseオブジェクトを作ってある。これは何回も送信するものだが、同時に1つしか使うことはなく、中身を入れ替えるだけなので使いまわすことにした。
次にsetRequestHandlerをしてある。クライアントサイドからのリクエストはBooleanで送っていたので、こちらもBooleanで受ける。
また、SessionManager.addSessionCreateHandlerでSession作成時のハンドラを登録する。
ハンドラメソッドでは受け取ったSessionオブジェクトのuserIdをServerTraceしている。これで新しいクライアントが接続されるたびにconnectedがView Logに出力される。クライアントサイドで表示したuserIdと見比べてみよう。一致するはずだ。
Booleanのリクエストに対するハンドラメソッドでは、まず現在のセッションを取得する。
そしてSessionのuserIdをServerTraceするので、クライアントがリクエストを送るたびにView Logで確認することができる。
さて、Sessionごとにアクセスした(リクエストを送った)回数を記録しよう。Sessionごとの情報なのでSession.setValueを使ってみる。
まずキー"access"があるかどうかを調べている。初めてのアクセスならundefinedが表示されるだろう。その場合はアクセス回数を示すuintに1をいれる。
キーがあった場合、そのキーの値を読み出し、uintにいれる。今回のアクセスで回数は増えたので+1しておこう。これが現在のこのセッションのアクセス回数だ。
setValueを使ってSessionに更新されたアクセス回数を記録する。
そしてサーバーサイドswfトータルのアクセス回数を+1する。これはどのクライアントが呼んでも+1されるようになっている。
最後にRequestオブジェクトに、送信する値を代入してsendRequestで送信しよう。
これでリクエストが来るたびにセッションのアクセス回数とサーバーサイドswfのアクセス回数を+1してクライアントに返す処理ができた。
session.getValue("access")==undefinedの判定は、実はonSessionCreate内で初期化して適当な値を書き込んでおけばすれば必要のない処理だったりもする。
ウィンドウを2つ開いて、適当にボタンを押しながらView Logを見て動作を確認しよう。
今回のプロジェクトのソースファイルはこちら。setEndPointの引数は変えてあるので注意。
今回はセッションを使ってみた。Sessionオブジェクトを利用すればクライアントに関連付けてデータを保持することが楽にできる。結構使うことになるだろう。
しかしaddSessionDisposeHandlerが使えないのが残念なところだ。バグだとしたら早くなおしてね。
次回はたぶん一斉送信のお話。
Author:9ballsyn
ActionScriptについて
最近はMolehill