Be a Supporter!

As3: Sockets (adobe Air Style)

  • 1,758 Views
  • 3 Replies
New Topic Respond to this Topic
egg82
egg82
  • Member since: Jun. 24, 2006
  • Offline.
Forum Stats
Member
Level 05
Game Developer
As3: Sockets (adobe Air Style) Oct. 7th, 2012 @ 10:51 PM Reply

---
AS3: Main
---

---
Category: Advanced
---

--- AS3: Sockets (Adobe AIR Style) ---

First thing's first: If you don't have it already, grab FlashDevelop

1. Project->New Project
2. Select "AIR AS3 Projector"
3. Name it whatever
4. Double-Click "Main.as" from the "src" folder in the "Project" tab on the far right
5. Create a new class called "Server" (right-click "src" folder, Add->New Class)
6. Create a new class called "Client"

We'll want to have the server running before we try to make connections to it (herp) so we'll do that first.

Open up Server.as and make these functions:

public function init():void{
	//this function initializes the server. We call this in Main
}
public function destroy():void{
	//this function destructs the server safely. We also call this in Main
}

public function checkTimers():void{
	//this function checks to see if it's time to do a bunch of things
}

private function onConnect(e:ServerSocketConnectEvent):void{
	//this is the function that runs automatically when someone connects to the server
}
private function onData(e:ProgressEvent):void{
	//this function runs when data is received
}

private function sortData(data:Object, address:String):void{
	//we use this function to sort through the data and figure out what to do with it
}
private function socketWrite(data:Object, address:String):void{
	//this function is used to send data back to the client, if necessary.
}

and these vars:

public var ready:Boolean = false; //this boolean flag just tells us when the server socket is ready to receive data

private var _socket:ServerSocket = new ServerSocket(); //this is the actual socket

private var _clients:Vector.<Socket> = new Vector.<Socket>; //this vector holds all the clients
private var _addresses:Vector.<String> = new Vector.<String>; //this vector holds all the addresses. It's faster than an address lookup through an array of objects

let's initialize the server:

public function init():void{
	_server.addEventListener(ServerSocketConnectEvent.CONNECT, onConnect); //this adds the event listener that gets client connections
	
	try{
		_socket.bind(7678); //this makes the server listen for data on port 7678. Obviously you can change this
	}catch(e:RangeError){
		trace("-Server- " + e.message); //here to stop the program from fataling
		return; //you shall not pass!
	}catch (e:ArgumentError) {
		trace("-Server- " + e.message); //here to stop the program from fataling
		return; //you shall not pass!
	}catch (e:IOError) {
		trace("-Server- " + e.message); //here to stop the program from fataling
		return; //you shall not pass!
	}
	
	try{
		_socket.listen(); //this actually starts the server
	}catch (e:RangeError) {
		trace("-Server- " + e.message); //here to stop the program from fataling
		return; //you shall not pass!
	}catch (e:IOError) {
		trace("-Server- " + e.message); //here to stop the program from fataling
		return; //you shall not pass!
	}
	
	_timers[0] = getTimer(); //used for client checking later
	
	ready = true; //server's ready
	
	trace("-Server- Listening..."); //tell us!
}

and in next logical order, the connection event:

private function onConnect(e:ServerSocketConnectEvent):void {
	for (var i:uint = 0; i < _addresses.length; ++i){ //going through the array to see if we already have that client. Don't want to listen to the same computer twice!
		if (e.socket.remoteAddress == _addresses[i]) { //looks like we already have it
			return; //so we just quit right here
		}
	}
	
	_clients[_addresses.length] = e.socket; //add all the juicy socket stuff to the vector
	_addresses[_addresses.length] = e.socket.remoteAddress; //add the address to another vector. Again, this is much faster than going through all the objects
	_clients[_addresses.length-1].addEventListener(ProgressEvent.SOCKET_DATA, onData); //add the event listener to receive data
}

and now to get some data!

private function onData(e:ProgressEvent):void {
	if (e.target.bytesAvailable > 0) { //make sure we actually have something to read
		try{
			var data:Object = e.target.readObject(); //read the data
		}catch (error:EOFError) {
			trace("-Server- Bad data from " + e.target.remoteAddress + ": " + error.message); //make sure the client can't crash the server
			return; //you shall not pass!
		}catch (error:IOError) {
			trace("-Server- Warning: Bad data from " + e.target.remoteAddress + ": " + error.message);//make sure the client can't crash the server
			return; //you shall not pass!
		}
		
		if(!data.action || !(data.action is String)){ //bad data. Don't process it because it doesn't ask us to do anything
			return; //you shall not pass!
		}
		
		sortData(data, e.target.remoteAddress); //now go through the data and do stuff
	}
}

and now we go through the data:

private function sortData(data:Object, address:String):void {
	var action:String = String(data.action).toLowerCase();
	
	if ("action" == "getHello") {
		data.data = "Well, hello, thar! :3";
		socketWrite(data, address);
	}
}

and finally we return what we've got:

private function socketWrite(input:Object, address:String):void {
	for (var i:uint = 0; i < _addresses.length; ++i) { //go through the addresses just in case we're getting "ghost" data
		if (_addresses[i] == address) { //found our guy!
			break; //break it here, I want to use that i
		}
	}
	
	if (i == _addresses.length - 1 && _addresses[i] != address) { //just in case the break was never called, check the last
		trace("-Server- Recieved data from " + address + " without prior connection");
		return; //you shall not pass!
	}
	
	if (_clients[i] == null || !_clients[i].connected) { //in case the client disconnected somewhere while we were processing
		_clients[i].removeEventListener(ProgressEvent.SOCKET_DATA, onData); //remove the event listener
		_clients = _clients.splice(i, 1); //remove the client
		_addresses = _addresses.splice(i, 1); //remove the address
		return; //you shall not pass!
	}
	
	try {
		_clients[i].writeObject(input); //write the data back to the client
		_clients[i].flush(); //flush the data, in case the system doesn't autoflush it for us
	}catch (error:IOError) {
		trace("-Server- " + error.message); //make sure we don't crash
		return; //you shall not pass!
	}
}

and now we check the timers for events:

public function checkTimers():void {
	var i:uint; //gimme an i! Gimme a... Oh, wait, that's it...
	var timer:int = getTimer(); //grab the current time
	
	if (timer - _timers[0] >= 30000) { //check the clients to see if they're all still there every 30 seconds
		for (i = 0; i < _addresses.length; ++i){ //go through the addresses
			if (!_clients[i].connected) { //we found one that's not connected
				_clients[i].removeEventListener(ProgressEvent.SOCKET_DATA, onData); //remove the listener
				_clients = _clients.splice(i, 1); //remove the client
				_addresses = _addresses.splice(i, 1); //remove the address
			}
		}
		_timers[0] = getTimer(); //reset the timer
	}
}

and now to destroy our creation:

public function destroy():void {
	if(ready){ //make sure we don't try to close it twice
		ready = false; //the server is no longer ready to accept connections because it's shutting down
		
		while(_addresses.length > 0){ //go through the addresses
			_clients[0].removeEventListener(ProgressEvent.SOCKET_DATA, onData); //remove the listener
			_clients = _clients.splice(0, 1); //remove the client
			_addresses = _addresses.splice(0, 1); //remove the address
		}
		
		if (_server.listening) { //make sure the server's actually listening before we try to stop it. Herp.
			try{
				_server.close(); //close the socket
			}catch (e:Error) {
				trace("-Server- " + e.message); //catch the error that probably will never come
			}
		}
	}
}

Programming stuffs (tutorials and extras)
PM me (instead of MintPaw) if you're confuzzled.
thank Skaren for the sig :P

BBS Signature
egg82
egg82
  • Member since: Jun. 24, 2006
  • Offline.
Forum Stats
Member
Level 05
Game Developer
Response to As3: Sockets (adobe Air Style) Oct. 7th, 2012 @ 11:15 PM Reply

And now the client. The client is much simpler than the server, so no worries there.

--- Client.as ---

It's kinda the same setup.

public function init():void {
	//initialize
}
public function destroy():void {
	//destroy
}

public function socketWrite(data:Object):void {
	//write
}

private function onConnect(e:Event):void {
	//connected to server
}
private function onData(e:Event):void {
	//received data
}
private function onClose(e:Event):void {
	//server closed the socket
}
public function onError(e:Event):void {
	//oops!
}
private function onTimeout(e:TimerEvent):void {
	//connection timeout
}

private function sortData(data:Object):void{
	//sort the data
}
public var ready:Boolean = false;

private var _timer:Timer = new Timer(5000, 1);
private var _socket:Socket = new Socket();

first thing's first, we gotta initialize the client:

private function init():void {
	_socket.addEventListener(Event.CLOSE, onClose); //what if the server says "no"?
	_socket.addEventListener(Event.CONNECT, onConnect); //ah, we connected!
	_socket.addEventListener(IOErrorEvent.IO_ERROR, onError); //aww, we gots an error 3:
	_socket.addEventListener(ProgressEvent.SOCKET_DATA, onData); //yay, data!
	
	_timer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimeout); //socket timeout
	_timer.start(); //start the timer
	
	try {
		_socket.connect("127.0.0.1", 7678); //connect to us on this port. Obviously can be changed.
	}catch (e:IOError) {
		_timer.reset(); //reset the timer so we don't go through timeout as well
		trace("-Client- " + e.message); //oops!
	}catch (e:SecurityError) {
		_timer.reset(); //reset the timer so we don't go through timeout as well
		trace("-Client- " + e.message); //oops!
	}
}

now: CONNECT!

private function onConnect(e:Event):void {
	_timer.reset(); //reset the timer so we don't go through timeout
	ready = true; //we're ready!
	trace("-Connect- Socket connected");
}

on nom nom, data!

private function onData(e:Event):void {
	_timer.reset(); //reset the timer so we don't go through timeout
	
	if (_socket.bytesAvailable > 0) { //make sure there's something to read
		try {
			var data:Object = _socket.readObject(); //read the data!
		}catch (e:EOFError) {
			trace("-Client- " + e.message); //what- how?
			return; //you shall not pass!
		}catch (e:IOError) {
			trace("-Client- " + e.message); //now this one I can see
			return; //you shall not pass!
		}
		
		sortData(data); //sort our data. No, we're not checking for bad data this time
	}
}

now let's sort the data:

private function sortData(data:Object):void {
	if (data.action == "getHello") {
		trace("Server said: " + data.data);
	}
}

server closed the connection? Whaaa?

private function onClose(e:Event):void {
	_timer.reset(); //reset the timer
	ready = false; //client is NOT ready!
	trace("-Client- Server closed connection"); //noooo.... Whaaaiii?!?
}

in case of error, run function:

private function onError(e:IOErrorEvent):void {
	_timer.reset(); //reset the timer
	trace("-Client- " + e.text); //oh, damn.
}
private function onTimeout(e:TimerEvent):void {
	destroy(); //remove the socket. Obviously something's wrong.
	trace("-Client- Read timeout"); //log it
}

and now to destroy our creation:

public function destroy():void {
	if (ready) { //don't want to accidentally run this again!
		ready = false; //client is NOT ready!
		
		_timer.stop(); //stop the timer
		
		if (_socket.connected) { //if we're connected, do stuff!
			_socket.removeEventListener(Event.CLOSE, onClose); //remove the listener
			_socket.removeEventListener(Event.CONNECT, onConnect); //remove the listener
			_socket.removeEventListener(IOErrorEvent.IO_ERROR, onError); //remove the listener
			_socket.removeEventListener(ProgressEvent.SOCKET_DATA, onData); //remove the listener
			
			_timer.removeEventListener(TimerEvent.TIMER_COMPLETE, onTimeout); //remove the listener
			
			try {
				_socket.close(); //close the socket
			}catch (e:IOError) {
				trace("-Client- " + e.message); //ya never know!
				return; //you shall not pass!
			}
		}
	}
}

Programming stuffs (tutorials and extras)
PM me (instead of MintPaw) if you're confuzzled.
thank Skaren for the sig :P

BBS Signature
egg82
egg82
  • Member since: Jun. 24, 2006
  • Offline.
Forum Stats
Member
Level 05
Game Developer
Response to As3: Sockets (adobe Air Style) Oct. 7th, 2012 @ 11:35 PM Reply

--- Client.as ctnd. ---

forgot the socketWrite function!

public function socketWrite(data:Object):void {
	try {
		_socket.writeObject(data); //write the data to the socket (server)
		_socket.flush(); //flush the socket just in case the OS doesn't do that for us
	}catch(e:IOError) {
		trace("-Client- " + e.message); //damn!
		return; //you shall not pass!
	}
	
	_timer.start(); //start the timeout timer
}

finally, in Main.as just wait for the server and client, and send the data after everything's connected.

--- Main.as ---

private var _server:Server;
private var _client:Client;
private function init(e:Event = null):void {
	removeEventListener(Event.ADDED_TO_STAGE, init);
	
	_server = new Server();
	addEventListener(Event.ENTER_FRAME, serverWait);
}

private function serverWait(e:Event):void{
	if(_server.ready){
		removeEventListener(Event.ENTER_FRAME, serverWait);
		
		_client = new Client();
		addEventListener(Event.ENTER_FRAME, clientWait);
	}
}

private function clientWait(e:Event):void{
	if(_client.ready){
		removeEventListener(Event.ENTER_FRAME, clientWait);
		
		var data:Object = new Object();
		data.action = "getHello";
		_client.socketWrite(data);
	}
}

Programming stuffs (tutorials and extras)
PM me (instead of MintPaw) if you're confuzzled.
thank Skaren for the sig :P

BBS Signature
egg82
egg82
  • Member since: Jun. 24, 2006
  • Offline.
Forum Stats
Member
Level 05
Game Developer
Response to As3: Sockets (adobe Air Style) Oct. 8th, 2012 @ 12:06 PM Reply

I typed all of this out in the posting box (some copypasta from an old project), so there's probably an error or two in there.

one of them is a coding error (my fault) - the application will freeze on shutdown if you're using a server.

in Server.as's destroy() function, change:

while(_addresses.length > 0){ //go through the addresses
			_clients[0].removeEventListener(ProgressEvent.SOCKET_DATA, onData); //remove the listener
			_clients = _clients.splice(0, 1); //remove the client
			_addresses = _addresses.splice(0, 1); //remove the address
		}

to

for (var i:uint = 0; i < _addresses.length; i++) { //go through the addresses
			_clients[i].removeEventListener(ProgressEvent.SOCKET_DATA, onData); //remove the listener
		}
		_clients = new Vector.<Socket>; //overwrite the clients with a new vector
		_addresses = new Vector.<String>; //overwrite the clients with a new vector

also, apparently i'm unfamiliar with how AS3's splice() function works with vectors. Could have sworn it returned a new vector with the data cut out...


Programming stuffs (tutorials and extras)
PM me (instead of MintPaw) if you're confuzzled.
thank Skaren for the sig :P

BBS Signature