Skip to content

Instantly share code, notes, and snippets.

@darrenfu
Last active July 19, 2016 14:23
Show Gist options
  • Save darrenfu/9a6202a73986207fa15e6c8ba9c16de0 to your computer and use it in GitHub Desktop.
Save darrenfu/9a6202a73986207fa15e6c8ba9c16de0 to your computer and use it in GitHub Desktop.
To resolve packet fragmentation and reassembly
package {
import flash.errors.IOError;
import flash.events.ErrorEvent;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.ProgressEvent;
import flash.events.SecurityErrorEvent;
import flash.net.Socket;
import flash.system.Security;
import flash.utils.ByteArray;
/**
* Handles the actual connection with the game room server.
*/
public class SocketConnection extends Object {
public static const SIZE_BYTES_SHORT:int = 2;
public static const SIZE_NULL_MARKER:int = 4;
private static const DEFAULT_SOCKET_TIMEOUT:int = 10000;
protected var mDone:Boolean = true;
private var mAttemptReconnect:Boolean = true;
private var mReconnectCount:int = 0;
private const MAX_RECONNECTS:int = 3;
private var mSocket:Socket;
private var mBufferData:ByteArray = new ByteArray();
/**
* Connects to the server.
*/
private function restartConnection():void {
mDone = false;
try {
if (mPolicyFile != null) Security.loadPolicyFile(mPolicyFile);
}
catch (e:Error) {
Log.info(".startConnection: error "+e.message, this, Log.WARN_TEXT_COLOR);
}
mSocket = new Socket();
mSocket.timeout = DEFAULT_SOCKET_TIMEOUT;
mSocket.addEventListener(Event.CONNECT, notifyConnected);
mSocket.addEventListener(Event.CLOSE, notifyDisconnected);
mSocket.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
mSocket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
mSocket.addEventListener(ProgressEvent.SOCKET_DATA, socketDataHandler);
try {
mSocket.connect(mAddr, mPort);
}
catch (e:SecurityError) {
Log.fatal(".startConnection: "+e.message, this);
reconnect(new SecurityErrorEvent(SecurityErrorEvent.SECURITY_ERROR, false, false, e.message));
}
}
/**
* Sends a message to the room server.
*
* @param p properties object containing message
*/
public function sendToServer(p:Properties):void {
if (mSocket.connected) {
// convert props to byte array
var buffer:ByteArray = new ByteArray();
for each (var key:String in p.getKeys()) {
buffer.writeUTF(key);
buffer.writeUTF(p.getProperty(key));
}
var obfus:ByteArray = obfuscate(buffer);
var msg:ByteArray = new ByteArray();
msg.writeShort(obfus.length);
msg.writeBytes(obfus);
try {
mSocket.writeBytes(msg);
mSocket.flush();
}
catch (e:IOError) {
Log.info(".sendToServer: error sending message: "+e, this, Log.WARN_TEXT_COLOR);
}
}
else {
Log.info(".sendToServer: error sending message: socket not connected", this, Log.WARN_TEXT_COLOR);
}
}
/**
* Reconnects to the server.
*/
private function reconnect(evt:Event):void {
Log.info("connection closed... ", this, Log.WARN_TEXT_COLOR);
if (! mDone && mAttemptReconnect && mReconnectCount++ < MAX_RECONNECTS) {
Log.info("reconnecting", this, Log.WARN_TEXT_COLOR);
restartConnection();
}
else {
Log.info("exiting", this, Log.WARN_TEXT_COLOR);
notifyDisconnectListeners(evt);
}
}
/**
* Obfuscates a properties object to be sent to the room server.
*
* @param p properties object
* @return byte array of obfuscated data
*/
private function obfuscate(byteArray:ByteArray):ByteArray {
var obByteArray:ByteArray = new ByteArray();
for (var i:int = 0; i < byteArray.length; ++i) {
obByteArray[i] = byteArray[i] ^ 0xFF;
}
return obByteArray;
}
/**
* Handles raw data from the socket.
*
* @param evt The associated event.
*/
private function socketDataHandler(event:ProgressEvent):void {
// read the data
mSocket.readBytes(mBufferData, mBufferData.length );
// read and dispatch messages
while (mBufferData.bytesAvailable > SIZE_BYTES_SHORT) {
var msgLen:int = mBufferData.readShort();
if (mBufferData.bytesAvailable >= msgLen) {
readMessage( msgLen );
// when closing this can be a problem
if (mBufferData == null) {
return;
}
if (mBufferData.bytesAvailable == 0) {
// ended at the end of message; reset the buffer
mBufferData.position = 0;
mBufferData.length = 0;
break;
}
} else {
// can't read a full message so move pos back and break until we get more data
mBufferData.position -= 2;
break;
}
}
}
/**
* Reads a message from the buffer and sends it to the mesage dispatcher.
*
* @param msgLen The message length.
*/
private function readMessage(msgLen:int):void {
// de-obfusacate
var startPos:int = mBufferData.position;
for (var i:int = startPos; i < startPos + msgLen; ++i) {
mBufferData[i] = mBufferData[i] ^ 0xFF;
}
// build dictionary
var msg:Properties = new Properties();
while (mBufferData.position < startPos + msgLen) {
var key:String = mBufferData.readUTF();
var val:String = mBufferData.readUTF();
msg.setProperty(key,val);
}
// dispatch
if (mDebug) {
logIn("<- "+msg.toString());
}
// reset the reconnect count when we get a good message
mReconnectCount = 0;
if (mMessageHandler != null) mMessageHandler.handleMessage(msg);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment