Adobe AIR で UDP Socket(DatagramSocket)


以前 ARDrone を Flash(AIR) で飛ばして遊んでるときに使用していた DatagramSocket。
これ、実案件では年に一回使うか使わないかだったりして、必要な時はいろいろ忘れているので…自分用メモ。

・ドキュメントクラス

package
{
	import com.bit101.components.InputText;
	import com.bit101.components.Label;
	import com.bit101.components.PushButton;
	import com.bit101.components.TextArea;

	import flash.desktop.NativeApplication;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.net.DatagramSocket;

	import utils.InterfaceUtil;

	[SWF( width = '330', height = '250', frameRate = '30', backgroundColor = '0xFFFFFF' )]


	/**
	 *
	 * @author mousepancyo
	 */
	public class UDPSocketEx extends Sprite
	{
		private var datagramSocket:DatagramSocket;

		private var _msg:TextArea;
		private var _targetInput:InputText
		private var _msgInput:InputText;
		private var _btSend:PushButton;

		private var _udpSocket:UDPSocketControler;

		public function UDPSocketEx()
		{
			setup();
			NativeApplication.nativeApplication.addEventListener( Event.EXITING, exit );
			_msg.text = "Bindable Addresses: \n" + InterfaceUtil.getBindableAddresses().toString();
		}


		private function setup():void
		{
			_udpSocket = new UDPSocketControler( InterfaceUtil.getBindableAddresses());
			_udpSocket.addEventListener( "receved", onDataReceived );
			_udpSocket.setup();

			_msg = new TextArea( this, 10, 80 );
			_msg.width = 310;
			var lab1:Label = new Label( this, 10, 201, "Target IP" );
			_targetInput = new InputText( this, 60, 201, _udpSocket.localIP );
			var lab2:Label = new Label( this, 10, 221, "Input Msg" );
			_msgInput = new InputText( this, 60, 221 );
			_msgInput.width = 150;
			_btSend = new PushButton( this, 220, 220, "Send Message", sendMsg );
			_msgInput.enabled = _targetInput.enabled = _btSend.enabled = false;

			var btConnect:PushButton = new PushButton( this, 60, 50, "Connect", onConnect );
			var btDisConnect:PushButton = new PushButton( this, 220, 10, "Dis Connect", onDisConnect );
			btDisConnect.enabled = false;
			var lab3:Label = new Label( this, 10, 11, "My IP" );
			var localInput:InputText = new InputText( this, 60, 11, _udpSocket.localIP );
			var lab4:Label = new Label( this, 10, 31, "Port No" );
			var portInput:InputText = new InputText( this, 60, 31, "55555" );

			function onConnect( e:MouseEvent ):void
			{
				btConnect.removeEventListener( MouseEvent.CLICK, onConnect );

				btConnect.enabled = localInput.enabled = portInput.enabled = false;
				_msgInput.enabled = _targetInput.enabled = _btSend.enabled = btDisConnect.enabled = true;

				//socketSetup();
				_udpSocket.connect( int( portInput.text ));
				_msg.text = _udpSocket.msg;

				btDisConnect.addEventListener( MouseEvent.CLICK, onDisConnect );
			}

			function onDisConnect( e:MouseEvent ):void
			{
				btDisConnect.removeEventListener( MouseEvent.CLICK, onDisConnect );
				btConnect.enabled = localInput.enabled = portInput.enabled = true;
				_msgInput.enabled = _targetInput.enabled = _btSend.enabled = btDisConnect.enabled = false;


				_udpSocket.disConnect();
				_msg.text = _udpSocket.msg;

				btConnect.addEventListener( MouseEvent.CLICK, onConnect );
			}
		}



		private function onDataReceived( e:Event ):void
		{
			_msg.text = _udpSocket.msg + "\n" + _msg.text;
		}



		private function sendMsg( e:MouseEvent ):void
		{
			var ip:String = _targetInput.text;
			var msg:String = _msgInput.text;

			_udpSocket.send( ip, msg );

		}


		private function exit( e:Event ):void
		{
			_udpSocket.disConnect();
		}
	}
}

・UDP接続用のクラス

package
{
	import flash.events.DatagramSocketDataEvent;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IEventDispatcher;
	import flash.net.DatagramSocket;
	import flash.utils.ByteArray;


	/**
	 *
	 * @author mousepancyo
	 */
	public class UDPSocketControler extends EventDispatcher
	{

		private var datagramSocket:DatagramSocket;
		private var _bindAdress:Array;

		public var msg:String;

		// My IPAdress
		private var _localIP:String;

		// Target IPAdress
		private var _targetIP:String;

		// Port Number
		private var _port:int;


		/**
		 * Getter
		 */
		public function get localIP():String
		{
			return _localIP;
		}

		public function get targetIP():String
		{
			return _targetIP;
		}




		/**
		 * Constractor
		 */
		public function UDPSocketControler( bindAdress:Array, target:IEventDispatcher = null )
		{
			super( target );

			_bindAdress = bindAdress;
		}


		public function setup():void
		{
			_localIP = _bindAdress[ 0 ];
			if ( _localIP.substr( 0, 3 ) == "127" )
			{
				_localIP = _bindAdress[ 1 ];
			}
		}


		/**
		 *  UDP Connect
		 */
		public function connect( port:int ):void
		{
			_port = port;

			datagramSocket = new DatagramSocket();

			if ( datagramSocket.bound )
			{
				datagramSocket.close();
			}

			datagramSocket.addEventListener( DatagramSocketDataEvent.DATA, onReceved );

			// Bind the socket
			datagramSocket.bind( _port, _localIP );

			// Listener
			datagramSocket.receive();

			msg = "Bound to: " + localIP + ":" + _port;
		}


		/**
		 *  DisConnect
		 */
		public function disConnect():void
		{
			try
			{
				datagramSocket.close();
				msg = "datagramsocket closed.";
			}
			catch ( e:Error )
			{
				msg = e.message;
			}
		}


		/**
		 *  Send
		 */
		public function send( ip:String, msg:String ):void
		{
			// message in ByteArray 
			var data:ByteArray = new ByteArray();
			data.writeUTFBytes( msg );

			// Send msg
			datagramSocket.send( data, 0, 0, ip, _port );
		}


		// Recever
		private function onReceved( e:DatagramSocketDataEvent ):void
		{
			msg = "Received from IP: " + e.srcAddress + "  Port: " + e.srcPort + "\n> " + e.data.readUTFBytes( e.data.bytesAvailable );
			dispatchEvent( new Event( "receved" ));
		}
	}
}

・InterfaceUtil クラス

package utils
{
    import flash.net.IPVersion;
    import flash.net.InterfaceAddress;
    import flash.net.NetworkInfo;
    import flash.net.NetworkInterface;

    public class InterfaceUtil
    {
        private static var bindableAddressesCache:Array;
        
        public static function getBindableAddresses():Array
        {
            if (bindableAddressesCache) return bindableAddressesCache;
            
            var addresses:Array = new Array();
            var netinfo:NetworkInfo = NetworkInfo.networkInfo;
            var netifs:Vector.<NetworkInterface> = netinfo.findInterfaces();
            
            for each (var netif:NetworkInterface in netifs)
            {
                for each (var addr:InterfaceAddress in netif.addresses)
                {
                    if (addr.ipVersion == IPVersion.IPV4) {
                        addresses.push(addr.address);
                    }
                }
            }
            
            bindableAddressesCache = addresses;
            return addresses;
        }
    }
}

動作確認用サンプルアプリはこちら。
Mac 用と Windows 用のキャプティブランタイム AIR アプリを一緒にアーカイブしてるのでちょっと容量が大きいです…