AIR.簡易Messengerを作る その3
メッセージ送受信部分を作って完成。
メッセージの受信
メッセージの受信は、
- ServerSocket待ち受け
- ServerSocketのConnectイベントで通信用Socket取得
- 受け取ったSocketにSocketData取得用のListener登録
- メッセージ受信
といった流れ。
initializeで待ち受け開始して、
private function initializeLogic():void { .... serverSocket = new ServerSocket(); serverSocket.addEventListener(ServerSocketConnectEvent.CONNECT, serverSocket_connectHandler); serverSocket.bind( PORT_TALK ); serverSocket.listen(); ... }
接続があったらSocketにListenerを登録して、
private function serverSocket_connectHandler(event:ServerSocketConnectEvent):void { event.socket.addEventListener(ProgressEvent.SOCKET_DATA, socket_socketDataHandler); }
メッセージを受信したら、テキストエリアに表示する。
private function socket_socketDataHandler(event:ProgressEvent):void { textArea.text = event.target.readUTF(); }
socketを保持する必要があるのかよく分かってないけど、
まあ、普通はこんな使い方はせんなぁ。
readUTFはreadUTFBytesの方が安全だけど、
とりあえずMessenger同士の通信には問題ないのでOK。
メッセージ送信
メッセージの送信は
- ListからIP取得
- Socketを接続
- 接続が完了したら、テキストエリアの文字列をSend
といった流れ。
ボタン押下をトリガーにIPを取得し、Socketをつなげて、
private function sendBtn_clickLogic():void { if( nodeList.selectedItem ) { var address:String = nodeList.selectedItem as String; var socket:Socket = new Socket(address, PORT_TALK); socket.addEventListener(Event.CONNECT, socket_connectHandler); socket.addEventListener(IOErrorEvent.IO_ERROR, socket_ioErrorHandler); } }
つながったら、メッセージを送信。
private function socket_connectHandler(event:Event):void { event.target.writeUTF( textArea.text ); event.target.flush(); event.target.close(); }
書き込んだらもういらないのでcloseする。
動作テスト
とりあえずこれで完成。
実際にメッセージのやりとりをしてみると、なかなか気持ちよく動いてくれる。
どうせなので、Win、Mac、Linuxの3台体制でテストしてみた。
TCP/IPのメッセージ送受信に関してはどれも問題なくできた。
ただ、Mac(10.5.8)ではLinuxと同じくbroadcastができなかった。
公開されているbeta2より進んだリリースならできるのかねぇ。
一応C言語でbroadcastしてみたら問題なかったので、
AIR 2.0 SDK beta2の問題だとおもふ。
あと、今回始めてDatagramSocketのbroadcastを試してみたけど、
思った以上に取りこぼしがある印象を受けた。
失敗するといっても低確率だと思っていたので、この動作は結構意外。
利用する場合は設計段階から留意しないと、はまるな。
簡易Messenger全ソース
<?xml version="1.0" encoding="utf-8"?> <mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" horizontalAlign="left" showStatusBar="false" fontSize="20" width="450" height="400" initialize="initializeLogic()" close="closeLogic()"> <!-- logic --> <mx:Script> <![CDATA[ import mx.collections.ArrayCollection; //-------------------------------------------------------------------------------------- // constants //-------------------------------------------------------------------------------------- /** node検出のbroadcastに使うポート */ static private const PORT_BROADCAST:int = 50000; /** メッセージ送受信に使うポート */ static private const PORT_TALK:int = 50001; /** node検出用にbroadcastするメッセージ */ static private const MSG_SEARCH_NODE:String = "message search node"; /** node検出メッセージに対して、返信するメッセージ */ static private const MSG_NODE_RES:String = "message node res"; //-------------------------------------------------------------------------------------- // logics //-------------------------------------------------------------------------------------- /** application initialize */ private function initializeLogic():void { try { // broadcastの受信開始 datagramSocket = new DatagramSocket(); datagramSocket.addEventListener(DatagramSocketDataEvent.DATA, datagramSocket_dataHandler); datagramSocket.bind( PORT_BROADCAST ); datagramSocket.receive(); // talkメッセージの待ち受け開始 serverSocket = new ServerSocket(); serverSocket.addEventListener(ServerSocketConnectEvent.CONNECT, serverSocket_connectHandler); serverSocket.bind( PORT_TALK ); serverSocket.listen(); } catch( e:Error ) { trace( e.message ); } } /** application close */ private function closeLogic():void { // リソース開放 datagramSocket.close(); serverSocket.close(); } /** search button click */ private function searchBtn_clickLogic():void { try { // node情報をクリアする nodes.removeAll(); // node検出用メッセージをbroadcast var ba:ByteArray = new ByteArray(); ba.writeUTF( MSG_SEARCH_NODE ); datagramSocket.send(ba, 0, 0, "192.168.0.255", PORT_BROADCAST); } catch( e:Error ) { trace( e.message ); // Unixはsendで例外 AIR 2.0 beta2 } } /** send button click */ private function sendBtn_clickLogic():void { // nodeの選択状態を確認 if( nodeList.selectedItem ) { // 選択されているなら、IPアドレス確保 var address:String = nodeList.selectedItem as String; // 選択したnodeにsocketをつなげる var socket:Socket = new Socket(address, PORT_TALK); socket.addEventListener(Event.CONNECT, socket_connectHandler); socket.addEventListener(IOErrorEvent.IO_ERROR, socket_ioErrorHandler); } } //-------------------------------------------------------------------------------------- // handlers //-------------------------------------------------------------------------------------- /** datagram socket data (broadcastメッセージの受け取り) */ private function datagramSocket_dataHandler(event:DatagramSocketDataEvent):void { try { // メッセージを確保 var msg:String = event.data.readUTF(); // broadcastメッセージか判定 if( msg == MSG_SEARCH_NODE ) { // node検出用のbroadcastメッセージなので、返信する // 送信データ作成 var ba:ByteArray = new ByteArray(); ba.writeUTF( MSG_NODE_RES ); // 送信 event.target.send(ba, 0, 0, event.srcAddress, PORT_BROADCAST); } else { // broadcastメッセージの返信なので、 // 送信元のIPアドレスをnode一覧に追加する nodes.addItem( event.srcAddress ); } } catch( e:Error ) { trace( e.message ); // UTF文字列じゃないと例外 } } /** server socket connect (Talkメッセージ受信用ServerSocketへの接続) */ private function serverSocket_connectHandler(event:ServerSocketConnectEvent):void { // socketを取り出して、メッセージ受け取り用Listenerを設定 event.socket.addEventListener(ProgressEvent.SOCKET_DATA, socket_socketDataHandler); } /** socket socket data (Talkメッセージの受信) */ private function socket_socketDataHandler(event:ProgressEvent):void { // メッセージを受け取ったら、メッセージをテキストエリアに表示 textArea.text = event.target.readUTF(); } /** sokcet connect (Talkメッセージ送信用Socketの接続) */ private function socket_connectHandler(event:Event):void { // テキストエリアの文字列を、送信 event.target.writeUTF( textArea.text ); event.target.flush(); event.target.close(); } /** socket io error (Talkメッセージ送信用Socketのエラー処理) */ private function socket_ioErrorHandler(event:IOErrorEvent):void { trace( "socket io error" ); } //-------------------------------------------------------------------------------------- // private members //-------------------------------------------------------------------------------------- /** broadcast用 */ private var datagramSocket:DatagramSocket; /** メッセージ受信用 */ private var serverSocket:ServerSocket; /** ローカルネット内のPC */ private const nodes:ArrayCollection = new ArrayCollection( ["192.168.0.10"] ); ]]> </mx:Script> <!-- components --> <mx:Button label="Search" width="140" click="searchBtn_clickLogic()"/> <mx:List id="nodeList" width="100%" height="60%" dataProvider="{nodes}"/> <mx:TextArea id="textArea" width="100%" height="40%"/> <mx:Button label="Send" width="100%" click="sendBtn_clickLogic()"/> </mx:WindowedApplication>