iOS Development: How can I encapsulate a string in an NSData object? - iphone

I'm building a multiplayer game on the iPhone and I need to send string data to the other players in the game. To do that, I need to encapsulate my NSString* string data in an NSData object somehow. Here's an example of how my code is structured...
typedef struct
{
PACKETTYPE packetType;
??? stringToSend; //<---not sure how to store this
} StringPacket;
StringPacket msg;
msg.packetType = STRING_PACKET;
msg.stringToSend = ... // <---not sure what to do here
NSData *packet = [NSData dataWithBytes:&msg length:sizeof(StringPacket)];
So my question is, if StringPacket is a struct defined in my header, what type should the stringToSend property be so that I can easily call the dataWithBytes method of NSData to encapsulate the packet data in an NSData object?
Thanks for your wisdom!

At first, you should convert your NSString to UTF8 representation via [NSString UTF8String].
After that, i'd recommend to store in packet string length, and after that - the string characters themself. All that can be done via appending NSData, created from char* via [NSData dataWithBytes:]
NSMutableData packet = [[NSMutableData alloc] init];
[packet appendBytes:&msg.packetType, sizeof(msg.packetType)];
char *str = [yourString UTF8String];
int len = strlen(str);
[packet appendBytes:(void*)&len, sizeof(len)];
[packet appendBytes:(void*)str, len];
To parse packet back, you should do:
NSData packet; // your packet
[packet getBytes:(void*)&packet.msg range:NSMakeRange(0, sizeof(packet.msg))];
int len;
[packet getBytes:(void*)&len range:NSMakeRange(sizeof(packet.msg), sizeof(len)];
NSData *strData = [packet subdataWithRange:NSMakeRange(sizeof(packet.msg) + sizeof(len)), packet.length];
NSString *str = [[NSString alloc] initWithData:strData encoding:UTF8Encoding];
There can be some mistakes since i'm writing from memory, but I think you'll get the idea.

If your strings have a maximum length, it's rather easy and can be done efficiently. So, assuming your strings max length for these packets is 255 and you've decided to use UTF-8 to encode your strings (both sides need to agree which encoding they're using), you could do it like this:
typedef struct
{
PACKETTYPE packetType;
uint8_t stringToSend[256]; // UTF8 string with max encoded length of 255 bytes
} StringPacket;
StringPacket msg;
msg.packetType = STRING_PACKET;
[theString getCString:msg.stringToSend maxLength:256 encoding:NSUTF8StringEncoding];
NSData *packet = [NSData dataWithBytes:&msg length:sizeof(StringPacket)];
Now you will have a proper C string in your packet that is at most 255 bytes of string data and the null terminator. Note, if your string can't be encoded to UTF8 in the size you gave it, the method will return NO, so your real code should actually check for that and handle it.
If you can't have a size limit, you can basically do the same thing, but you have to deal with dynamically allocating the memory, copying the bytes, creating the data and properly freeing the memory at the right time, so it becomes much more involved but it's the same basic idea. See also the method -getBytes:maxLength:usedLength:encoding:options:range:remainingRange: on NSString, it can be very useful in generating these messages where the string size is dynamic and totally unknown.
For the most simple case, however, the code above should get the job done.

Related

sending dynamic data

I define a struct for the move
typedef struct {
MsgType msgType;
int newFallenStonesSize;
char *newFallenStones;
} MsgMove;
And send the data like this:
MsgMove message;
message.msgType = MsgTypeMove;
message.newFallenStones = (char *)malloc(nrNewFallenStones*sizeof(char));
for (int i=0; i<nrNewFallenStones; i++) {
message.newFallenStones[i]=newFallenStones[i];
}
message.newFallenStonesSize = nrNewFallenStones;
NSData *data = [NSData dataWithBytes:&message length:(2*sizeof(int)+message.newFallenStonesSize*sizeof(char))];
[[KKGameKitHelper sharedGameKitHelper] sendDataToAllPlayers:data reliable:YES];
the data is correct at the moment of sending, but when I receive it like this:
else if (msg->msgType == MsgTypeMove)
{
MsgMove *msgMove = (MsgMove *) [data bytes];
for (int i=0; i<msgMove->newFallenStonesSize; i++) {
NSLog(#"New Stone received:%i",msgMove->newFallenStones[i]);
}
}
The values have changed. For example 1, 6, 3 and I receive 76, 105 98.
Anyone knows why this is happening?
The main issue is that you are sending a pointer to your data over the network rather than the actual data. When you malloc space for message.newFallenStones it will be set to some apparently random location in memory that is not adjacent to your MsgMove structure. You then write your data in this other memory location. What you package up to transfer is the MsgMove structure (with a pointer to somewhere else in memory) plus whatever random bytes happens to immediately follow it in memory.
The typical way this is handled is to instead have your entire message be malloc'ed together and write the data into the end of it. More like:
typedef struct {
MsgType msgType;
int newFallenStonesSize;
char newFallenStones; // The first newFallenStones value
} MsgMove;
and then send with
MsgMove *message;
message = (MsgMove *)malloc(sizeof(MsgMove)+nrNewFallenStones-1);
message->msgType = MsgTypeMove;
message->newFallenStonesSize = nrNewFallenStones;
char *newStones = &MsgMove->newFallenStones;
for (int i=0; i<nrNewFallenStones; i++) {
newStones[i]=newFallenStones[i];
}
NSData *data = [NSData dataWithBytes:message length:(sizeof(MsgMove)+nrNewFallenStones-1)];
[[KKGameKitHelper sharedGameKitHelper] sendDataToAllPlayers:data reliable:YES];

parsing NSData object for information

I have a NSData object coming back from my server, it varies in its content but sticks to a particular structure.
I would like to know (hopfully with some example code) how to work though this object to get the data I need out of it.
the structure of the data objects inside the objects are like this
leading value (UInt16) - (tells me what section of the response it is)
Size of string (UInt32) or number - (UInt32)
String (not null terminated) i.e. followed by the next leading value.
I have been reading through the Binary Data Programming Guide however that's only really showing me how to put my data into new NSData objects and accessing and compairing the bytes.
The thing I am stuck on is how do I say grab the info dynamically. Check the NSdata objects first leading value figure out if its string or int then get the string or int and move onto the next leading value..
any suggestions or example code would be really helpfull.. just stuck in abit of a mind block as I have never attempted anything like this in objective C.
Some of this depends on how your server is written to encode the data into what it is sending you. Assuming it is encoding the numeric values using standard network byte ordering (big-endian) you will want it converted to the correct byte-ordering for iOS (I believe that is always little-endian).
I would approach it something like this:
uint16_t typeWithNetworkOrdering, typeWithLocalOrdering;
uint32_t sizeWithNetworkOrdering, sizeWithLocalOrdering;
char *cstring = NULL;
uint32_t numberWithNetworkOrdering, numberWithLocalOrdering;
const void *bytes = [myData bytes];
NSUInteger length = [myData length];
while (length > 0) {
memcpy(&typeWithNetworkOrdering, bytes, sizeof(uint16_t));
bytes += sizeof(uint16_t);
length -= sizeof(uint16_t);
memcpy(&sizeWithNetworkOrdering, bytes, sizeof(uint32_t));
bytes += sizeof(uint32_t);
length -= sizeof(uint32_t);
typeWithLocalOrdering = CFSwapInt16BigToHost(typeWithNetworkOrdering);
sizeWithLocalOrdering = CFSwapInt32BigToHost(sizeWithNetworkOrdering);
if (typeWithLocalOrdering == STRING_TYPE) { // STRING_TYPE is whatever type value corresponds to a string
cstring = (char *) malloc(sizeWithLocalOrdering + 1);
strncpy(cstring, bytes, sizeWithLocalOrdering);
cstring[sizeWithLocalOrdering] = '\0';
NSString *resultString = [NSString stringWithCString:cstring encoding:NSUTF8StringEncoding];
NSLog(#"String = %#", resultString);
free(cstring);
bytes += sizeWithLocalOrdering;
length -= sizeWithLocalOrdering;
// Do whatever you need to with the string
}
else if (typeWithLocalOrdering == NUMBER_TYPE) { // NUMBER_TYPE is whatever type value corresponds to a number
memcpy(&numberWithNetworkOrdering, bytes, sizeof(uint32_t));
numberWithLocalOrdering = CFSwapInt32BigToHost(numberWithNetworkOrdering);
NSLog(#"Number = %u", numberWithLocalOrdering);
bytes += sizeof(uint32_t);
length -= sizeof(uint32_t);
// Do whatever you need to with the number
}
}
Define your own internal structs and cast the pointer to it:
NSData* data;
struct headerType
{
uint16_t type;
uint32_t length;
};
const struct headerType* header=(const struct headerType*)[data bytes]; // get the header of the response
if (header->type==1)
{
const char* text=((const char*)header)+6; // skip the header (16bits+32bits=6 bytes offset)
}
EDIT:
If you need to read them in a loop:
NSData* data;
const uint8_t* cursor=(const uint8_t*)[data bytes];
while (true)
{
uint16_t type=*((uint16_t*)cursor);
cursor+=2;
if (cursor==1)
{
// string
uint32_t length=*((uint32_t*)cursor);
cursor+=4;
const char* str=(const char*)cursor;
cursor+=length;
}
else if (cursor==2)
{
// another type
}
else
break;
}

iOS/Objective C: SHA-1 and Base64

I have to convert the phrase '1234' into the Base64-encoding of its SHA-1 hash.
I want to have: '1234' = cRDtpNCeBiql5KOQsKVyrA0sAiA=
(This example work in connection with the server communication)...
But when I checked it manualley on these sites or use my Methods in my XCode Project the result is always different:
1234
After SHA-1 (http://www.sha1.cz/)
7110eda4d09e062aa5e4a390b0a572ac0d2c0220
After Base64 (http://base64-encoder-online.waraxe.us/)
NzExMGVkYTRkMDllMDYyYWE1ZTRhMzkwYjBhNTcyYWMwZDJjMDIyMA==
This is my SHA-1 function:
- (NSString *)sha1:(NSString *)str {
const char *cStr = [str UTF8String];
unsigned char result[CC_SHA1_DIGEST_LENGTH];
CC_SHA1(cStr, strlen(cStr), result);
NSString *s = [NSString stringWithFormat:
#"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
result[0], result[1], result[2], result[3], result[4],
result[5], result[6], result[7],
result[8], result[9], result[10], result[11], result[12],
result[13], result[14], result[15],
result[16], result[17], result[18], result[19]
];
return [s lowercaseString];
}
For Base64 I use a this class: http://cocoawithlove.com/2009/06/base64-encoding-options-on-mac-and.html
This is the call of SHA-1 and Base64:
NSString *pwHash =[self sha1:self._txtFieldPW.text]; //In my case '1234'
NSLog(#"Hash: %#",pwHash); //7110eda4d09e062aa5e4a390b0a572ac0d2c0220
//7110eda4d09e062aa5e4a390b0a572ac0d2c0220
NSData *pwHashData = [[NSData alloc]initWithData:[pwHash dataUsingEncoding:1]];
NSString *base64 = [pwHashData base64Encoding];
NSLog(#"Base64: %#",base64);
//NzExMGVkYTRkMDllMDYyYWE1ZTRhMzkwYjBhNTcyYWMwZDJjMDIyMA==
What is going wrong?
Hash is a binary value. The "cRDtpNCeBiql5KOQsKVyrA0sAiA=" string is a result of the binary value of the hash, encoded. Meanwhile, the "NzExMGVkYTRkMDllMDYyYWE1ZTRhMzkwYjBhNTcyYWMwZDJjMDIyMA==" string is the result of the Base64 transform on the hexadecimal presentation of the hash.
Did that make sense? So you have a SHA1 hash, an array of 20 bytes: {0x71, 0x10, ...}. You can Base64-encode that chunk of memory as it is. Or you can convert each byte into two lowercase hex digits, then you'll get the ASCII string "7110eda4d09e062aa5e4a390b0a572ac0d2c0220". If you apply Base64 encoding to that string, as opposed to the original hash bytes, you'll get the Base64 value "NzExMGV...".
EDIT, with extra binary goodness: reformulate the second snippet thus:
unsigned char result[CC_SHA1_DIGEST_LENGTH];
const char *cStr = [self._txtFieldPW.text UTF8String];
CC_SHA1(cStr, strlen(cStr), result); //Now result contains the hash
//Wrap the result in a NSData object
NSData *pwHashData = [[NSData alloc] initWithBytes:result length: sizeof result];
//And take Base64 of that
NSString *base64 = [pwHashData base64Encoding];
NSLog(#"Base64: %#",base64);
And get rid of the sha1 method. Once you remove the monstrous stringWithFormat call, it's a two-liner. If you really want the hex hash in the log, be my guest; but don't treat the hex string as the true value of the hash, 'cause it's not.
Unless you're using ARC, don't forget to free the NSData object afterwards.

encrypted data return nil string

I am using Rijndael Encryption Algorithm when I am going to encrypt it, it encrypted in NSData. I want that encrypted NSdata into NSString. I tried to convert it into string but it return nil. Have anyone any solutions to get into string.
I am doing like this
NSString *passphrase = #"super-secret";
NSStringEncoding myEncoding = NSUTF8StringEncoding;
NSString *alphaPlain = #"This is a encryption test.";
NSData *alphaDataPlain = [alphaPlain dataUsingEncoding:myEncoding];
NSLog(#" SimpleText value : %#",alphaPlain);
NSData *alphaDataCypher = [alphaDataPlain AESEncryptWithPassphrase:passphrase];
NSString *alphaStringCypher = [[NSString alloc] initWithData:alphaDataCypher encoding:myEncoding];
NSLog(#" Encrypted value : %#",alphaStringCypher);
It returns nil value.
Thanks
The encrypted data is no longer a UTF8 string, it's just some sequence of bytes, so decoding it as UTF8 fails.
What do you want to do with the string? If it's just for logging/debugging purposes, you could use [myData description] to get a hex string (with some extra whitespace for better readability). If you need this to transfer the data in a context where you need a textual representation, converting it to Base64 would be a good idea, see this answer for an easy way to do that.

How to Convert NSInteger or NSString to a binary (string) value

Anybody has some code in objective-c to convert a NSInteger or NSString to binary string?
example:
56 -> 111000
There are some code in stackoverflow that try do this, but it doesn´t work.
Thanks
Not sure which examples on SO didn't work for you, but Adam Rosenfield's answer here seems to work. I've updated it to remove a compiler warning:
// Original author Adam Rosenfield... SO Question 655792
NSInteger theNumber = 56;
NSMutableString *str = [NSMutableString string];
for(NSInteger numberCopy = theNumber; numberCopy > 0; numberCopy >>= 1)
{
// Prepend "0" or "1", depending on the bit
[str insertString:((numberCopy & 1) ? #"1" : #"0") atIndex:0];
}
NSLog(#"Binary version: %#", str);
Tooting my own horn a bit here...
I've written a math framework called CHMath that deals with arbitrarily large integers. One of the things it does is allows the user to create a CHNumber from a string, and get its binary representation as a string. For example:
CHNumber * t = [CHNumber numberWithString:#"56"];
NSString * binaryT = [t binaryStringValue];
NSLog(#"Binary value of %#: %#", t, binaryT);
Logs:
2009-12-15 10:36:10.595 otest-x86_64[21918:903] Binary value of 56: 0111000
The framework is freely available on its Github repository.