commit 9a85e5b90a37c8c989cda04f69862b82f25ca924 Author: David Fifield david@bamsoftware.com Date: Sat Jun 11 21:26:55 2011 -0700
Add an RTMFPSocket class. --- RTMFPSocket.as | 209 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 209 insertions(+), 0 deletions(-)
diff --git a/RTMFPSocket.as b/RTMFPSocket.as new file mode 100644 index 0000000..e23c4fd --- /dev/null +++ b/RTMFPSocket.as @@ -0,0 +1,209 @@ +/* +The RTMFPSocket class provides a socket-like interface around RTMFP +NetConnection and NetStream. Each RTMFPSocket contains one NetConnection and two +NetStreams, one for reading and one for writing. + +To create a listening socket: + var rs:RTMFPSocket = new RTMFPSocket(url, key); + rs.addEventListener(Event.COMPLETE, function (e:Event):void { + // rs.id is set and can be sent out of band to the client. + }); + rs.addEventListener(RTMFPSocket.ACCEPT_EVENT, function (e:Event):void { + // rs.peer_id is the ID of the connected client. + }); + rs.listen(); +To connect to a listening socket: + // Receive peer_id out of band. + var rs:RTMFPSocket = new RTMFPSocket(url, key); + rs.addEventListener(Event.CONNECT, function (e:Event):void { + // rs.id and rs.peer_id are now set. + }); +*/ + +package +{ + import flash.events.Event; + import flash.events.EventDispatcher; + import flash.events.IOErrorEvent; + import flash.events.NetStatusEvent; + import flash.events.ProgressEvent; + import flash.net.NetConnection; + import flash.net.NetStream; + import flash.utils.ByteArray; + + public class RTMFPSocket extends EventDispatcher + { + public static const ACCEPT_EVENT:String = "accept"; + + public var connected:Boolean; + + private var nc:NetConnection; + private var incoming:NetStream; + private var outgoing:NetStream; + + /* Cache to hold the peer ID between when connect is called and the + NetConnection exists. */ + private var connect_peer_id:String; + + private var buffer:ByteArray; + + private var cirrus_url:String; + private var cirrus_key:String; + + public function RTMFPSocket(cirrus_url:String, cirrus_key:String) + { + connected = false; + + buffer = new ByteArray(); + + this.cirrus_url = cirrus_url; + this.cirrus_key = cirrus_key; + + nc = new NetConnection(); + } + + public function get id():String + { + return nc.nearID; + } + + public function get peer_id():String + { + return incoming.farID; + } + + /* NetStatusEvents that aren't handled more specifically in + listen_netstatus_event or connect_netstatus_event. */ + private function generic_netstatus_event(e:NetStatusEvent):void + { + switch (e.info.code) { + case "NetConnection.Connect.Closed": + dispatchEvent(new Event(Event.CLOSE)); + break; + case "NetStream.Connect.Closed": + connected = false; + close(); + break; + default: + var event:IOErrorEvent = new IOErrorEvent(IOErrorEvent.IO_ERROR); + event.text = e.info.code; + dispatchEvent(event); + break; + } + } + + private function listen_netstatus_event(e:NetStatusEvent):void + { + switch (e.info.code) { + case "NetConnection.Connect.Success": + outgoing = new NetStream(nc, NetStream.DIRECT_CONNECTIONS); + outgoing.client = { + onPeerConnect: listen_onpeerconnect + }; + outgoing.publish("server"); + + /* listen is complete, ready to accept. */ + dispatchEvent(new Event(Event.COMPLETE)); + break; + case "NetStream.Connect.Success": + break; + default: + return generic_netstatus_event(e); + break; + } + } + + private function listen_onpeerconnect(peer:NetStream):Boolean { + incoming = new NetStream(nc, peer.farID); + incoming.client = { + r: receive_data + }; + incoming.play("client"); + + connected = true; + dispatchEvent(new Event(ACCEPT_EVENT)); + + return true; + } + + private function connect_netstatus_event(e:NetStatusEvent):void + { + switch (e.info.code) { + case "NetConnection.Connect.Success": + outgoing = new NetStream(nc, NetStream.DIRECT_CONNECTIONS); + outgoing.publish("client"); + + incoming = new NetStream(nc, connect_peer_id); + incoming.client = { + r: receive_data + }; + incoming.play("server"); + break; + case "NetStream.Connect.Success": + connected = true; + dispatchEvent(new Event(Event.CONNECT)); + break; + default: + return generic_netstatus_event(e); + break; + } + } + + /* Function called back when the other side does a send. */ + private function receive_data(bytes:ByteArray):void { + var event:ProgressEvent; + + event = new ProgressEvent(ProgressEvent.SOCKET_DATA); + event.bytesLoaded = bytes.bytesAvailable; + + bytes.readBytes(buffer, buffer.length, bytes.bytesAvailable); + if (bytes.bytesAvailable == 0) { + /* Reclaim memory space. */ + bytes.position = 0; + bytes.length = 0; + } + + dispatchEvent(event); + } + + public function listen():void + { + nc.addEventListener(NetStatusEvent.NET_STATUS, listen_netstatus_event); + nc.connect(cirrus_url, cirrus_key); + } + + public function connect(peer_id:String):void + { + /* Store for later reading by connect_netstatus_event. */ + this.connect_peer_id = peer_id; + + nc.addEventListener(NetStatusEvent.NET_STATUS, connect_netstatus_event); + nc.connect(cirrus_url, cirrus_key); + } + + public function close():void + { + outgoing.close(); + incoming.close(); + nc.close(); + } + + public function readBytes(output:ByteArray, offset:uint = 0, length:uint = 0):void + { + buffer.readBytes(output, offset, length); + } + + public function writeBytes(input:ByteArray, offset:uint = 0, length:uint = 0):void + { + var sendbuf:ByteArray; + + /* Read into a new buffer, in case offset and length do not + completely span input. */ + sendbuf = new ByteArray(); + sendbuf.writeBytes(input, offset, length); + + /* Use a short method name because it's sent over the wire. */ + outgoing.send("r", sendbuf); + } + } +}