AS3 / AIR readObject() from socket - How do you check all data has been received? - actionscript-3

If you write a simple object to a socket:
var o:Object = new Object();
o.type = e.type;
o.params = e.params;
_socket.writeObject(o);
_socket.flush();
Then on the client do you simply use:
private function onData(e:ProgressEvent):void
{
var o:Object = _clientSocket.readObject();
}
Or do you have to implement some way of checking all of the data has been received recieved before calling .readObject()

There's 2 approaches:
If you're confident that your object will fit into one packet, you can do something like:
var fromServer:ByteArray = new ByteArray;
while( socket.bytesAvailable )
socket.readBytes( fromServer );
fromServer.position = 0;
var myObj:* = fromServer.readObject();
If you have the possibility of having multiple packet messages, then a common usage is to prepend the message with the length of the message. Something like (pseudo code):
var fromServer:ByteArray = new ByteArray();
var msgLen:int = 0;
while ( socket.bytesAvailable > 0 )
{
// if we don't have a message length, read it from the stream
if ( msgLen == 0 )
msgLen = socket.readInt();
// if our message is too big for one push
var toRead:int = ( msgLen > socket.bytesAvailable ) ? socket.bytesAvailable : msgLen;
msgLen -= toRead; // msgLen will now be 0 if it's a full message
// read the number of bytes that we want.
// fromServer.length will be 0 if it's a new message, or if we're adding more
// to a previous message, it'll be appended to the end
socket.readBytes( fromServer, fromServer.length, toRead );
// if we still have some message to come, just break
if ( msgLen != 0 )
break;
// it's a full message, create your object, then clear fromServer
}
Having your socket able to read like this will mean that multiple packet messages will be read properly as well as the fact that you won't miss any messages where 2 small messages are sent almost simultaneously (as the first message will treat it all as one message, thereby missing the second one)

Rule # 1 when dealing with TCP: it is an octet stream transfer protocol. You may never ever assume anything about how many octets (8 bit long values, commonly called bytes) you get in one go, always write code that can deal with any amount, both too few and too many. There is no gurantee that the write will not be split into multiple reads. There is also no gurantee that a single read will be from a single write.

The way I handled it was to make a call back that the server tell the client that the null bit was received.
The null bit is appended to the end of the data string you are sending to the server.
String.fromCharCode(0)
Also in your case you are doing
_socket.writeObject(o);
You should be sending a string not an object.
So Like this.
_socket.writeUTFBytes( 'Hellow World" + String.fromCharCode(0) );
NOTE *************
And one thing that most first time socket creators over look is the
fact that the first request to from the client to the server over the
port that the socket is connected on is a request for the
crossdomainpolicy.xml

If you only wish to send Objects, the simplest solution is if you send an int(size) before every object. Its not important to send the exact size, you can send a bit less. In my case, I've sent a bitmapdata, and the width and height of the object. obviously the bitmapdata's size is so big, its okay if you send only that, and ignore the rest.
var toRead=0;
protected function onSocketData(event:ProgressEvent):void{
if(toRead==0) {
toRead=socket.readInt()
}
if(socket.bytesAvailable>toRead){
var recieved:Object=socket.readObject()
/*do stuff with recieved object*/
toRead=0
}

Related

How to use a signal as function parameter in CAPL

I am trying to write a function in CAPL that takes a signal and calculates the physical value with the signal value, the signal factor and the signal offset.
This is how a simple gateway normally works:
message CAN1.myMessage1 myMessage1 = {DIR = RX};//message from the database
message CAN2.myMessage2 myMessage2 = {DIR = TX};//another message from the database
on message CAN1.*
{
if(this.id == myMessage1.id)
{
myMessage1 = this;
myMessage2.mySignalB = myMessage1.mySignalA * myMessage1.mySignalA.factor + myMessage1.mySignalA.offset;
}
}
And this is what I am trying to do:
...
on message CAN1.*
{
if(this.id ==myMessage1.id)
{
myMessage1 = this;
myMessage2.mySignalB = PhysicalValue(myMessage1.mySignalA);
}
}
double PhysicalValue(signal * s)
{
return s*s.factor+s.offset;
}
There are two problems with this code:
Firstly when I pass the signal as the parameter the compiler says that the types don't match. The second problem is that inside the function the attributes (factor and offset) are no longer recognized.
These problems might have something to do with the weird object-oriented-but-not-really nature of CAPL. The value of the signals can be accessed directly but it also has attributes?
int rawValue = myMessage1.mySignalA;
If you are familiar with C you might say that the problem is that I am specifying a pointer in the function but that I am not passing a pointer into it. But in CAPL there are no pointers and the * simply means anything.
Without the * I would have needed to use a specific signal which would have defeated the purpose of the function.
EDIT:
I have found the attribute .phys by now which does exactly what my demo function would have done.
double physValue = myMessage1.mySignalA.phys;
This has already made my code much shorter but there are other operations that I need to perform for multiple signals so being able to use signals as a function parameter would still be useful.
What you can do is this:
double PhysicalValue(signal * s)
{
// access signal by prepending a $
return $s.phys;
}
Call like this
on message CAN1.*
{
if(this.id ==myMessage1.id)
{
myMessage1 = this;
myMessage2.mySignalB = PhysicalValue(CAN1::myMessage1::mySignalA);
}
}
I.e. when you call your function, you have to provide the qualified name of the signal (with colons rather than dots). To my knowledge it is not possible to use myMessage1.mySignalA, since signals itself are not a CAPL datatype.
Apart from this, you might re-think whether you really should be using on message, but rather switch to on signal. Handling the signal values no matter with which message they are sent is done by CANoe's signal server.
Note that CANoe already has a function which does exactly what you're trying to do (multiplying by factor and adding offset). It's called getSignal:
on message CAN1.*
{
if(this.id == myMessage1.id)
{
myMessage2.mySignalB = getSignal(myMessage1::mySignalA);
}
}
Offsets and factors are defined in e.g. the DBC files.

How to use node streams to insert big data into mysql?

I'm trying to use node stream to insert 10 million records into mysql. Is there a way to do this with node stream? I'm not finding very useful or 'friendly' answers or documentation about this issue anywhere. So far I'm able to insert 45K records, but I'm getting some errors trying with a record set any bigger than that.
Also, what's the callback in the code below supposed to do here? I'm not sure where I got this code from and I'm not actually passing a call back, so, maybe that's the problem!! :D Any ideas? What would the callback actually be? Maybe the callback is supposed to take chunk and pass a chunk at a time? How could I rework this to get it to work consistently? I just don't think this code below is actually splitting the data up into chunks at all. How do I split it up into manageable chunks?
Depending on the amount of records I try this with I get different errors. The errors I am getting are:
For 50K - 80K sometimes I get this error:
Error: connect ETIMEDOUT
at Connection._handleConnectTimeout
I get this error for 100K records or above:
Error: ER_NET_PACKET_TOO_LARGE: Got a packet bigger than 'max_allowed_packet' bytes
at Query.Sequence._packetToError
This error for around 55K records:
Error: write EPIPE
at WriteWrap.afterWrite [as oncomplete] (net.js:788:14)
It's kind of wild to get 3 different errors depending on the amount of records I'm trying to insert.
Here's the code (It's working fine for 45000 records, but not for anything bigger):
var db = require('./index.js');
var faker = require('faker');
var stream = require('stream');
var songs = [];
var size = 10000000;
var songList = function(){
for (var i = 0; i < size; i++) {
var song = [i, faker.random.words(1,2), faker.name.findName(), faker.internet.url(1,50), faker.random.words(1,2), faker.random.words(1,20)];
songs.push(song);
}
console.log('songs', songs);
return songs;
}
var songSql = "INSERT INTO songs (song_id, song_name, artist, song_url, song_album, song_playlist) VALUES ?";
var songValues = songList();
var songSeed = function() {
console.log('x: ', x);
var query = db.connection.query(songSql, [songValues]).stream({highWaterMark: 5});
var testStream = new stream.Transform({highWaterMark: 5, objectMode: true});
testStream._write = function(chunk,encoding,callback) {
setTimeout(function() {
console.log('my chunk: ', chunk);
callback();
},1000);
}
// Pipe the query stream into the testStream
query.pipe(testStream)
// Monitor data events on the side to see when we pause
query.on("result",function(d,i) {
console.log("Data Sent")
})
}
songSeed();
On the MySQL server increase max_allowed_packet to 1G. There's no real downside to this.

AS3 How to Send Multiple Messages in writeUTF?

So with TCP in AS3, I'm trying to write strings over to the server and then to the clients, but it appears to only be able to read one at a time. This is not good because I'm sending messages based on keys being pressed, and the client has an action that needs to be taken place based on what key is pressed. Since multiple keys can be pressed at a time, obviously it does not work correctly.
Client example:
if (keys[p1_jump_key])
{
p1_up = "down";
sock.writeUTF("p1_up_down");
sock.flush();
}
else
{
p1_up = "up";
sock.writeUTF("p1_up_up");
sock.flush();
}
if (keys[p1_crouch_key])
{
p1_down = "down";
sock.writeUTF("p1_down_down");
sock.flush();
}
else
{
p1_down = "up";
sock.writeUTF("p1_down_up");
sock.flush();
}
And then here is the server:
function socketDataHandler(event:ProgressEvent):void{
var socket:Socket = event.target as Socket;
var message:String = socket.readUTF();
for each (var socket:Socket in clientSockets)
{
socket.writeUTF(message);
socket.flush();
}}
And finally, here is the recieving client (I have a method that allows the server to differentiate between the two):
if(msg=="p1_down_down"){
p1_down="down";
}
if(msg=="p1_down_up"){
p1_down="up";
}
if(msg=="p1_up_down"){
p1_down="down";
}
if(msg=="p1_up_up"){
p1_down="up";
}
Now many of you already see the issue, as when the down key is up, it sends the message "p1_down_up". When the up key is up, it sends the message "p1_up_up". Both messages are sending at once when neither of them are being pressed. The receiving client is, I suppose, just getting one of the signals, or perhaps neither of them. How do I make MULTIPLE signals get wrote and read over the server? I tried using an array but you can't write those apparently. Thank you.
For anyone else who comes across this like I did trying to figure out how to accomplish this, I have to say that the other solution supplied is highly inefficient, overly complicated and too hard to read. Instead of adding a null byte to the end of your message and pushing packets into arrays, you can simply have the server send the length of the entire message to determine whether the message is in multiple packets, and to make sure you read multiple writes in the correct order.
Here is a working client receive function. I send data as compressed Objects converted to ByteArrays. Objects can store any type of data which make them a good candidate for transferring complex information from server to client.
var messageBytes: ByteArray = new ByteArray;
var messageLength: int = 0;
var readBuffer = false;
function receiveTCP(e: ProgressEvent): void {
while (e.target.bytesAvailable >= 4) {//Loop to see if there is data in the socket that is available to read.
if (messageLength == 0) {
messageLength = e.target.readUnsignedInt();//read the first 4 bytes of data to get the size of the message and store it as our message length.
readBuffer = true;//set the readBuffer to true. While this is true, the function wont be reading any more data from the server until the current message is finished being read.
}
if (messageLength <= e.target.bytesAvailable) {//check to see if our message is finished being read.
if (readBuffer == true) {//Make sure another message isn't being read. Without this check, another incoming message from the server will overwrite the current one from being executed.
e.target.readBytes(messageBytes, 0, messageLength);//reads the byte array into our messageBytes ByteArray declared at the top.
messageBytes.uncompress();//decompresses our message
var message: Object = new Object();
message = messageBytes.readObject();//Writes our ByteArray into an object again.
receiveMessage(message);//send our message to another function for the data to be executed.
messageLength = 0;//reset our messageLength for the next message.
readBuffer = false;//reset our buffer so that the client can read the next message being received, or the next message in line.
} else {
receiveTCP(e);//if there is another message in wait and the previous one isn't finished reading, execute the same function with the same event data again. Eventually the buffer will open and the message will be read.
}
} else {
break;//If the message is split up into multiple packets, this will reset the loop to continue reading the message until all the packets are accounted for.
}
}
}
This will prevent Clogging, allow you to receive multiple messages simultaneously and put together messages that were delivered in multiple packets.
Admittedly, this took a while to figure out. If you need the serverside code for sending the message, let me know.
The TCP protocol consists of a bi-directional stream of bytes, and as such message boundaries are not preserved. Due to this, if you are sending messages too often, you can end up reading from the buffer two messages instead of one.
Thus, some of the messages you are sending might be being processed together, which fails the comparison test on the receiving end.
To fix this, you can append a delimiter at the end of each message. This way, you'll know where every message you sent begins and ends, allowing you to split and process them separately. This also means you don't need to flush after every write to the buffer.
For instance, you could use a null byte (\0) as delimiter.
if(keys[p1_jump_key])
{
p1_up = "down";
sock.writeUTF("p1_up_down\0");
}
...
sock.flush();
And on the receiving end, we trim any trailing delimiters and then split the message into the smaller packets.
EDIT: Fixing the issue you described gives rise to another one: your messages might be being split over two buffer reads. I've edited my code to reflect a solution to this, by creating a temporary variable where partial messages are stored, to be prepended to the other part(s) of the message in the following reads.
// First we check to see if there are any 'parts' of a message that we've stored temporarily from the last read. If so, prepend them to the message string, which must be its continuation
if(temp != "")
{
msg = temp + msg;
temp = "";
}
// Now we begin processing our packet
var splitPackets:Array = msg.split("\0");
var packets:Array = new Array();
// Deal with incomplete message reads
if(splitPackets[splitPackets.length - 1] != "")
{
// If the last item in our array isn't empty, it means out message stream ended in a partial message. Hence, we have to store this value temporarily, and append it on the next read of the stream
temp = splitPackets[splitPackets.length - 1];
}
// If the last item in our array is empty, it means our message stream ends in a full message
for(var i = 0; i < splitPackets.length - 1; i++)
{
packets[i] = splitPackets[i];
}
for(var i = 0; i < packets.length; i++)
{
if(packets[i] == "p1_down_down")
{
// Process each packet individually here
...
// If you have already processed the packet, move on to the next
continue;
}
// Processing code for other packets
...
}
Hope this helps!

Save Number with Shared Object and Add to that Saved Number.

Hey everyone so basically what I am trying to accomplish is saving a number with the Shared Object which the the coins that the player collects in the game and if they player quits out of the game and comes back to play again the amount of coins he had when he quit will still show and he will be able to add to that amount if the player picks up more coins. I had this working for a bit but then the textfield started displaying "NAN".
Here is how I have it set up.
The Variable private var nCoins:Number;
In the constructor function I have:
sharedObjectCoins = SharedObject.getLocal("CoinsData");
nCoins = 0 + sharedObjectCoins.data.tCoins;
if (sharedObjectCoins.data.tCoins == null)
{
sharedObjectCoins.data.tCoins = nCoins;
}else
{
trace("Save data found."); // if we did find data...
loadDataTimeAttack(); // ...load the data
}
and in the games Enter.Frame Loop I have the function saveDataCoins which is setup like so:
private function saveDataCoins():void
{
if (nCoins > sharedObjectCoins.data.tCoins )
{
sharedObjectCoins.data.tCoins = nCoins;
}
coinsGraphic.coinsText.text = " " + sharedObjectCoins.data.tCoins;
sharedObjectCoins.flush();
}
not sure if you need the function to where the hitTest takes place between the coins and player but here it is:
private function checkPlayerHitCoins():void
{
for (var i:int = 0; i < aCoinsArray.length; i++)
{
//get current point in i loop
var currentCoins:mcCoin = aCoinsArray[i];
//test if player is hitting current point
if(player.hitTestObject(currentCoins))
{
nCoins += 1;
updatecoinsTextScore();
updateCoinsPauseScreen();
//Add points sound effects
var coinsSEffect:Sound = new coinsSound();
coinsSEffect.play();
//remove point on stage
currentCoins.destroyCoins();
//remove points from array
aCoinsArray.splice(i, 1);
trace("Hit: " + aCoinsArray.length);
}
}
}
Please if anyone could help me with this maybe point something out that I am doing wrong. This code worked perfect one time and when I closed the screen and came back to re test it the textfield displayed NAN and thats it when I hitTest the coins sometimes the NAN switches to a number for like a second but then goes back to NAN.
The first time (or rather every time it creates a new shared object) you will be trying to add undefined to 0, which will result in either a runtime error or NaN.
You need to check if the value exists before attempting to do addition with it.
if(sharedObjectCoints.data && sharedObjectCoins.data.tCoins && !isNaN(sharedObjectCoins.data.tCoins)){
nCoins = Number(sharedObjectCoins.data.tCoins); //there's not point in adding 0
trace("Save data found."); // if we did find data...
loadDataTimeAttack(); // ...load the data
}else{
sharedObjectCoins.data.tCoins = nCoins;
}
Also, if you don't manually set a value to a number var, it will start off life as NaN. eg var nCoins:Number will be NaN until you set it to something.
That said, working with the sharedObject directly like this is a very sloppy way to code your program. Really you should just use shared object to load and save the value, and everything in between use a strongly typed variable.
var nCoins:int = 0;
var tCoins:int = 0;
sharedObjectCoins = SharedObject.getLocal("CoinsData");
if(sharedObjectCoins.data && sharedObjectCoins.data.tCoins && !isNaN(sharedObjectCoins.data.tCoins){
tCoins = int(sharedObjectCoins.data.tCoins);
}else{
//no shared object, use default value for tCoins
tCoins = 0; //or whatever it should start off as.
}
Then write a save function
private function saveSharedObject():void {
sharedObjectCoins.data.tCoins = tCoins;
sharedObjectCoins.flush();
}
Then replace all other instances of sharedObjectCoins.data.tCoins with the var tCoins
It's probably best not to flush the shared object every frame for performance purposes.
Also, shared objects may or may not actually save, depending on user preferences, storage space available, etc. They should not be relied upon for critical data retention.
You can listen for problems with the shared object with AsyncErrorEvent.ASYNC_ERROR I believe (It's been a while since I've worked with AS3 Shared Objects)

Binary SMS in Symbian

I wonder if anyone has managed to create a working code for sending out binary messages (to configure Symbian phones) and have also some binary data sample.
So far all the samples I have found fail to leave the Outbox or never return.
// Current entry is the Draft folder.
iSmsMtm->SwitchCurrentEntryL( KMsvDraftEntryId );
// Create a new SMS message entry as a child of the current context.
iSmsMtm->CreateMessageL( KUidMsgTypeSMS.iUid );
CMsvEntry& serverEntry = iSmsMtm->Entry();
TMsvEntry entry( serverEntry.Entry() );
/* Send Binary SMS */
CSmsHeader &hdr = iSmsMtm->SmsHeader();
CSmsMessage &msg = hdr.Message();
CSmsPDU &pdu = msg.SmsPDU();
CSmsUserData &userdata = pdu.UserData();
// Set the DCS byte
pdu.SetBits7To4(TSmsDataCodingScheme::ESmsDCSTextUncompressedWithNoClassInfo);
pdu.SetAlphabet(TSmsDataCodingScheme::ESmsAlphabet8Bit);
pdu.SetClass(ETrue, TSmsDataCodingScheme::ESmsClass2);
char buf[]= {...}; //my binary data, 247 bytes long
// Construct a dummy message
HBufC8 * iMessage = HBufC8::NewL(300);
TPtr8 TempUDHBufDesc((TUint8*)buf,247,247);
iMessage->Des().Copy(TempUDHBufDesc);
_LOGFENTRY1(_L("mess length %d"),iMessage->Des().Length());
userdata.SetBodyL(*iMessage);
delete iMessage;
// Message will be sent immediately.
entry.SetSendingState( KMsvSendStateWaiting );
entry.iDate.UniversalTime(); // insert current time //Solution for HomeTime()
// Set the SMS message settings for the message.
CSmsHeader& header = iSmsMtm->SmsHeader();
CSmsSettings* settings = CSmsSettings::NewL();
CleanupStack::PushL( settings );
settings->CopyL( iSmsMtm->ServiceSettings() ); // restore settings
settings->SetDelivery( ESmsDeliveryImmediately ); // to be delivered immediately
settings->SetDeliveryReport(EFalse);
settings->SetCharacterSet(TSmsDataCodingScheme::ESmsAlphabet8Bit); // IMPORTANT! For sending binary SMS
header.SetSmsSettingsL( *settings ); // new settings
// Let's check if there is a service center address.
if ( header.Message().ServiceCenterAddress().Length() == 0 )
{
// No, there isn't. We assume there is at least one service center
// number set and use the default service center number.
CSmsSettings* serviceSettings = &( iSmsMtm->ServiceSettings() );
// Check if number of service center addresses in the list is null.
if ( !serviceSettings->ServiceCenterCount() )
{ _LOGENTRY("No SC");
return ; // quit creating the message
}
else
{
CSmsNumber* smsCenter= CSmsNumber::NewL();
CleanupStack::PushL(smsCenter);
smsCenter->SetAddressL((serviceSettings->GetServiceCenter( serviceSettings->DefaultServiceCenter())).Address());
header.Message().SetServiceCenterAddressL( smsCenter->Address() );
CleanupStack::PopAndDestroy(smsCenter);
}
}
CleanupStack::PopAndDestroy( settings );
// Recipient number is displayed also as the recipient alias.
entry.iDetails.Set( _L("+3725038xxx") );
iSmsMtm->AddAddresseeL( _L("+3725038xxx") , entry.iDetails );
// Validate message.
if ( !ValidateL() )
{ _LOGENTRY("Not valid");
return ;
}
entry.SetVisible( ETrue ); // set message as visible
entry.SetInPreparation( EFalse ); // set together with the visibility flag
serverEntry.ChangeL( entry ); // commit changes
iSmsMtm->SaveMessageL(); // save message
TMsvSelectionOrdering selection;
CMsvEntry* parentEntry = CMsvEntry::NewL( iSmsMtm->Session(), KMsvDraftEntryId, selection );
CleanupStack::PushL( parentEntry );
// Move message to Outbox.
iOperation =parentEntry->MoveL( entry.Id(), KMsvGlobalOutBoxIndexEntryId, iStatus );
CleanupStack::PopAndDestroy( parentEntry );
iState = EWaitingForMoving;
SetActive();
Mostly I'm not sure about the correct values for port and class . Also some correct binary string would be nice to have for testing. Now I'm not sure if thecode is bad or the data.
Use the JSR120 specification and the wireless toolkit. they contain java example code that will work for sure.
These are implemented directly using RSocket objects in Symbian C++.
If you really want to do it in C++, the simplest way is to copy your TMsvEntry to the entry of the sms service. In your code above, that means using "iSmsMtm->ServiceId()" instead of "KMsvGlobalOutBoxIndexEntryId". also, just copy the message to the service but do move it to the outbox after it has been successfully sent.
shameless plug : http://www.quickrecipesonsymbianos.com will contain an explanation of the Symbian C++ messaging API will simple and reusable example code.
The solution that worked is to use RComm and "DATAPORT::1" to send out the binary SMS using AT commands (like using a modem).
If you want to send the SMS silently (and avoid the complexity of using the messaging APIs), you should send it via an RSocket: http://wiki.forum.nokia.com/index.php/How_to_send_an_SMS_using_sockets
Depending on your needs this might be more suitable than using the messaging APIs.