Skip navigation
This discussion is archived

Sending Strings Over GKSession - How to stop some craziness?

2982 Views 7 Replies Latest reply: Sep 15, 2009 7:38 AM by Tech Guru RSS
Tech Guru Level 2 Level 2 (205 points)
Currently Being Moderated
Sep 13, 2009 11:17 PM
Sounds simple, right? But the tricky part is I am trying to retain the header structure from GKTank... I am quite desperate for help at this point as I've been flailing around on this all day and all night....

All my other events use this structure already and work just fine..and after a ton of trial and error I'm very close... hoping I can get one of you experts to take a look. The issue is that when my strings are less than 8 characters, the results get very strange....

Here's the relevant code: (kMaxPacketSize is a constant of 1024)
To send Data: (this is straight from GKTank, actually)

- (void)sendNetworkPacket:(GKSession *)session packetID:(int)packetID withData:(void *)data ofLength:(int)length reliable:(BOOL)howtosend {

static unsigned char networkPacket[kMaxPacketSize];
const unsigned int packetHeaderSize = 2 * sizeof(int); // we have two "ints" for our header

if(length < (kMaxPacketSize - packetHeaderSize)) { // our networkPacket buffer size minus the size of the header info
int *pIntData = (int *)&networkPacket[0];
// header info
pIntData[0] = packetNumber++;
pIntData[1] = packetID;

// copy data in after the header
memcpy( &networkPacket[packetHeaderSize], data, length );

NSData *packet = [NSData dataWithBytes: networkPacket length: (length+8)];

if(howtosend == YES) {
[session sendDataToAllPeers:packet withDataMode:GKSendDataReliable error:nil];
} else {
[session sendDataToAllPeers:packet withDataMode:GKSendDataUnreliable error:nil];
}
}


In this particular case, I began by calling the above method thusly:

NSString *myText = self.myLabel.text
NSInteger len = [myText length];
[self sendNetworkPacket:mySession packetID:NETWORKTEXTEVENT withData:myText ofLength:(len*2)+1 reliable:YES];
}


and unwrapping like this on the other end:

NSString *pStringData = (NSString *)&incomingPacket[8];


Works like a charm... strings show up just fine on the other device.... except when the string is less than 8 characters...this causes crashes of EXCBADACCESS on the receiving device. When I check values for these short strings prior to sending over GKSession, the memcpy command does not properly embed the string into 'networkPacket' and keeps remnants from the last string that was transmitted.

So, in an attempt to work around what is probably a flaw in my logic, I decided to see what happens if I pad the beginning and end of these short strings to make them over 8 characters, by defining another string 'spacer' and then sending a padded string instead of myText:

NSString *spacer = @"zZzZ";
NSString *bufText = [spacer stringByAppendingString:[myText stringByAppendingString:spacer]];
len = [bufText length];
[self sendNetworkPacket:mySession packetID:NETWORKTEXTEVENT withData:bufText ofLength:(len*2)+1 reliable:YES];


This got rid of the issues with memcpy, and now all strings are embedded in 'networkPacket' properly, some just have the spacers on the beginning and end... no problem, right? I figured I'd just filter out the spacers on the other end, but guess what? I have even tried using the same code to disassemble the packet, prior to sending it, exactly the way I do on the receiving device... it disassembles fine, and I can re-create the string from the raw packet I send....

However... (and this ONLY happens on the strings that have been padded) when I receive the data through GKSession I get totally random characters and even lengths! Sometimes I get odd system strings like 'tpenable' or 'uni0164' ... it's so bizarre... I don't understand what is making the padded strings get treated differently.... I tried padding all strings just to check and then they all act like this when transmitted... and again when I use the following code, prior to sending the packet, both test1 and test2 look fine:

NSString *test1 = (NSString *)&networkPacket[8];
unsigned char *testPacket = (unsigned char *)[packet bytes];
NSString *test2 = (NSString *)&testPacket[8];
NSLog (@"netPacket");
NSLog (test1);
NSLog (@"Packet");
NSLog (test2);



If you have any ideas, I'd really appreciate it!

Message was edited by: Tech Guru
MacBook Pro, iPhone OS 3.0
  • RayNewbie Level 5 Level 5 (6,810 points)
    Hey Tech - There's no way I can analyze all your code tonight, but I'm seeing a few things that are puzzling:

    NSString *myText = self.myLabel.text
    NSInteger len = [myText length];
    [self sendNetworkPacket:mySession packetID:NETWORKTEXTEVENT withData:myText ofLength:(len*2)+1 reliable:YES];
    // ...

    - (void)sendNetworkPacket:(GKSession *)session packetID:(int)packetID withData:(void *)data ofLength:(int)length reliable:(BOOL)howtosend {
    // ...
    // copy data in after the header
    memcpy( &networkPacket[packetHeaderSize], data, length );

    It looks like the data param has been set equal to a pointer to a NSString instance and you're passing that pointer to memcpy. Do we think the text is at that address? It looks like that's what's expected since memcpy is asked to copy len*2+1 bytes, starting at that address, into the packet, where len is the number of unichars of text. Not sure what the extra byte is for. Do we think the unichars in NSString are terminated? I think you're depending on zeros in the networkPacket buffer to provide the termination.

    It might be interesting to know what bytes are in that buffer after memcpy.

    Then it looks like the code that reads the packet expects to find a valid NSString object in there:

    NSString *test1 = (NSString *)&networkPacket[8];

    This code seems to be making all kinds of assumptions about the structure of NSString. If some of them are correct I guess I learned a lot about the internals of that class this evening. But wouldn't it make a lot more sense to just use the interface that's provided? I.e. just extract the text from the NSString object in the format of your choice, and copy that text into the packet?

    - Ray
    MacBook, Mac OS X (10.5.5)
  • RayNewbie Level 5 Level 5 (6,810 points)
    [dataString bytes] returns a +const void*+ type. The compiler is complaining because you passed that +const void*+ pointer to a method that declared the 3rd arg to be a void*. The difference between the two pointers is that the first type doesn't want you to change the bytes that it's pointing to, while the second type doesn't care.

    For example:

    - myMethod:(const char *)foo {
    foo[3] = 'A'; // changing the value of the 4th byte at address foo
    }

    In the above, the compiler should warn that your method changed the data which was referenced by a const char* type.

    But sendNetworkPacket doesn't do anything naughty like the above, it just copies the data at the given addy into a buffer. So you could have declared the data param as const void* like this:

    - (void)sendNetworkPacket:(GKSession *)session packetID:(int)packetID withData:(const void *)data ofLength:(int)length reliable:(BOOL)howtosend;

    The above change should get rid of the warning. If the compiler thinks you're changing that data in sendNetworkPacket (we know you're not doing that, but it's hard to argue with a compiler), you'll then get a new warning to that effect. In that case you might as well put a type cast in the call. I.e. take 'const' back out of the method declaration, and cast the arg in the call like this:

    [self sendNetworkPacket:mySession packetID:NETWORKTEXTEVENT withData:(void*)[dataString bytes] ofLength:len reliable:YES];

    The first try is preferable, since we like to avoid type casts, and since (I assume) sendNetworkPacket really doesn't want to change that data. But if the compiler won't cooperate, the second way will always work for you.

    Btw, putting the text into a NSData object is fine by me, but you could've also used any number of NSString methods that aren't deprecated (e.g. [myText UTF8String]). No need to go there now though. I'm guessing your early problems with some perfectly good methods were due to data type glitches. Because of mismatched data types, you may have abandoned the correct approach and gone down the wrong path.

    Once things got to the point where a NSString pointer was sent to memcpy, I think all of us were at risk of falling into darkness. I stared at the code for a very long time, wondering if everyone in the World but me knew that trick. Next time you get a data type error, maybe post the code here and let us try and help out while the problem is still easy to solve, ok?

    All the best,
    Ray
    MacBook, Mac OS X (10.5.5)
  • PlanetOfTheApes Calculating status...
    ON the subject of this topic, may i ask what the reason for it being incomingPacket[8] is.

    I am trying to create my own structure as in GK Tank and pass it through but am being created with errors and crashing aplenty. I too have started descending into the darkness and am getting to a point where I have no idea where to go from here.

    If someone could please explain what is going on with these packets then I would be greatly appreciative.

    Thank You
    iPhone OS 3.0.1

Actions

More Like This

  • Retrieving data ...

Bookmarked By (0)

Legend

  • This solved my question - 10 points
  • This helped me - 5 points
This site contains user submitted content, comments and opinions and is for informational purposes only. Apple disclaims any and all liability for the acts, omissions and conduct of any third parties in connection with or related to your use of the site. All postings and use of the content on this site are subject to the Apple Support Communities Terms of Use.