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;
}
Related
If I have a character string, how can I convert the values to hexadecimal in Objective-C? Likewise, how can I convert from a hexadecimal string to a character string?
As an exercise and in case it helps, I wrote a program to demonstrate how I might do this in pure C, which is 100% legal in Objective-C. I used the string-formatting functions in stdio.h to do the actual conversions.
Note that this can (should?) be tweaked for your setting. It will create a string twice as long as the passed-in string when going char->hex (converting 'Z' to '5a' for instance), and a string half as long going the other way.
I wrote this code in such a way that you can simply copy/paste and then compile/run to play around with it. Here is my sample output:
My favorite way to include C in XCode is to make a .h file with the function declarations separate from the .c file with implementation. See the comments:
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
// Place these prototypes in a .h to #import from wherever you need 'em
// Do not import the .c file anywhere.
// Note: You must free() these char *s
//
// allocates space for strlen(arg) * 2 and fills
// that space with chars corresponding to the hex
// representations of the arg string
char *makeHexStringFromCharString(const char*);
//
// allocates space for about 1/2 strlen(arg)
// and fills it with the char representation
char *makeCharStringFromHexString(const char*);
// this is just sample code
int main() {
char source[256];
printf("Enter a Char string to convert to Hex:");
scanf("%s", source);
char *output = makeHexStringFromCharString(source);
printf("converted '%s' TO: %s\n\n", source, output);
free(output);
printf("Enter a Hex string to convert to Char:");
scanf("%s", source);
output = makeCharStringFromHexString(source);
printf("converted '%s' TO: %s\n\n", source, output);
free(output);
}
// Place these in a .c file (named same as .h above)
// and include it in your target's build settings
// (should happen by default if you create the file in Xcode)
char *makeHexStringFromCharString(const char*input) {
char *output = malloc(sizeof(char) * strlen(input) * 2 + 1);
int i, limit;
for(i=0, limit = strlen(input); i<limit; i++) {
sprintf(output + (i*2), "%x", input[i]);
}
output[strlen(input)*2] = '\0';
return output;
}
char *makeCharStringFromHexString(const char*input) {
char *output = malloc(sizeof(char) * (strlen(input) / 2) + 1);
char sourceSnippet[3] = {[2]='\0'};
int i, limit;
for(i=0, limit = strlen(input); i<limit; i+=2) {
sourceSnippet[0] = input[i];
sourceSnippet[1] = input[i+1];
sscanf(sourceSnippet, "%x", (int *) (output + (i/2)));
}
output[strlen(input)/2+1] = '\0';
return output;
}
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];
I am doing a file uploading job. I want to generate SHA256 and CRC32 hashes. Can anyone help me how shall I generate those hash? I want to get it working for iOS.
SHA256 is available in CommonCrypto. CRC32 is not a hash, it a Cyclic Redundancy Check.
Example code:
#import <CommonCrypto/CommonDigest.h>
NSData *dataIn = [#"Now is the time for all good computers to come to the aid of their masters." dataUsingEncoding:NSASCIIStringEncoding];
NSMutableData *macOut = [NSMutableData dataWithLength:CC_SHA256_DIGEST_LENGTH];
CC_SHA256(dataIn.bytes, dataIn.length, macOut.mutableBytes);
NSLog(#"dataIn: %#", dataIn);
NSLog(#"macOut: %#", macOut);
NSLog output:
dataIn: <4e6f7720 69732074 68652074 696d6520 666f7220 616c6c20 676f6f64 20636f6d 70757465 72732074 6f20636f 6d652074 6f207468 65206169 64206f66 20746865 6972206d 61737465 72732e>
macOut: <53f89cf6 7ebfbe56 89f1f76a 3843dfd1 09d68c5b a938dcd2 9a12004e 108260cb>
For both of these, you can use this gist:
https://gist.github.com/paul-delange/6808278
And an example
NSString* crc32 = (__bridge NSString*)TGDFileHashCreateWithPath((__bridge CFStringRef)filepath, TGDFileHashDefaultChunkSizeForReadingData, TGDChecksumAlgorithmCRC32);
This method will generate crc32c as used by gcloud on iOS from a filepath. If you want the standard crc32 just uncomment the other value for CRC32_POLYNOMIAL.
It reads the file given in 512KB chunks so can be used on large files.
- (NSString*) crc32c:(NSString*)filepath{
/// using crc code from
// http://classroomm.com/objective-c/index.php?action=printpage;topic=2891.0
// by rgronlie
//this is the standard crc32 polynomial
//uint32_t CRC32_POLYNOMIAL = 0xEDB88320;
//this is the crc32c one
uint32_t CRC32_POLYNOMIAL = 0x82F63B78L;
uint32_t CRC32C_SEED = 0xFFFFFFFFL;
// create and populate a lookup table
uint32_t* pCRCTable = malloc(sizeof(uint32_t) * 256);
for (uint32_t i = 0; i <= 255; i++)
{
uint32_t crc32 = i;
for (uint32_t j = 8; j > 0; j--)
{
if ((crc32 & 1) == 1)
crc32 = (crc32 >> 1) ^ CRC32_POLYNOMIAL;
else
crc32 >>= 1;
}
pCRCTable[i] = crc32;
}
// get a handle to the file
NSFileHandle *filehandle = [NSFileHandle fileHandleForReadingAtPath:filepath];
if(filehandle == NULL){
NSLog(#"failed to create file handle");
return nil;
}
// a buffer to read into
NSData* databuffer;
uint32_t crc = CRC32C_SEED;
// read the file in chunks of 512KB
while(true){
databuffer = [filehandle readDataOfLength: 512 * 1024];
// if there is nothing left finish
if([databuffer length] == 0){
break;
}
// otherwise run each chunk through the lookup table
uint8_t *pBytes = (uint8_t *)[databuffer bytes];
uint32_t length = [databuffer length];
while (length--)
{
crc = (crc>>8) ^ pCRCTable[(crc & 0xFF) ^ *pBytes++];
}
}
// clean up
[filehandle closeFile];
free(pCRCTable);
// this is the result
uint32_t hash = crc ^ 0xFFFFFFFFL;
// reverse it for endianness
uint32_t hash_reversed = CFSwapInt32HostToBig(hash);
// as raw bytes
NSData* hash_data = [NSData dataWithBytes: &hash_reversed length: sizeof(hash_reversed)];
// return base64 encoded
return [hash_data base64EncodedStringWithOptions:0];
}
there are no apps which can generate Hash for ios
This should work....its for Mac
http://itunes.apple.com/us/app/digiprint/id473233587?mt=12
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.
I have an extremely strange bug.
I have two applications that communicate over TCP/IP.
Application A is the server, and application B is the client.
Application A sends a bunch of float values to application B every 100 milliseconds.
The bug is the following: sometimes some of the float values received by application B are not the same as the values transmitted by application A.
Initially, I thought there was a problem with the Ethernet or TCP/IP drivers (some sort of data corruption). I then tested the code in other Windows machines, but the problem persisted.
I then tested the code on Linux (Ubuntu 10.04.1 LTS) and the problem is still there!!!
The values are logged just before they are sent and just after they are received.
The code is pretty straightforward: the message protocol has a 4 byte header like this:
//message header
struct MESSAGE_HEADER {
unsigned short type;
unsigned short length;
};
//orientation message
struct ORIENTATION_MESSAGE : MESSAGE_HEADER
{
float azimuth;
float elevation;
float speed_az;
float speed_elev;
};
//any message
struct MESSAGE : MESSAGE_HEADER {
char buffer[512];
};
//receive specific size of bytes from the socket
static int receive(SOCKET socket, void *buffer, size_t size) {
int r;
do {
r = recv(socket, (char *)buffer, size, 0);
if (r == 0 || r == SOCKET_ERROR) break;
buffer = (char *)buffer + r;
size -= r;
} while (size);
return r;
}
//send specific size of bytes to a socket
static int send(SOCKET socket, const void *buffer, size_t size) {
int r;
do {
r = send(socket, (const char *)buffer, size, 0);
if (r == 0 || r == SOCKET_ERROR) break;
buffer = (char *)buffer + r;
size -= r;
} while (size);
return r;
}
//get message from socket
static bool receive(SOCKET socket, MESSAGE &msg) {
int r = receive(socket, &msg, sizeof(MESSAGE_HEADER));
if (r == SOCKET_ERROR || r == 0) return false;
if (ntohs(msg.length) == 0) return true;
r = receive(socket, msg.buffer, ntohs(msg.length));
if (r == SOCKET_ERROR || r == 0) return false;
return true;
}
//send message
static bool send(SOCKET socket, const MESSAGE &msg) {
int r = send(socket, &msg, ntohs(msg.length) + sizeof(MESSAGE_HEADER));
if (r == SOCKET_ERROR || r == 0) return false;
return true;
}
When I receive the message 'orientation', sometimes the 'azimuth' value is different from the one sent by the server!
Shouldn't the data be the same all the time? doesn't TCP/IP guarantee delivery of the data uncorrupted? could it be that an exception in the math co-processor affects the TCP/IP stack? is it a problem that I receive a small number of bytes first (4 bytes) and then the message body?
EDIT:
The problem is in the endianess swapping routine. The following code swaps the endianess of a specific float around, and then swaps it again and prints the bytes:
#include <iostream>
using namespace std;
float ntohf(float f)
{
float r;
unsigned char *s = (unsigned char *)&f;
unsigned char *d = (unsigned char *)&r;
d[0] = s[3];
d[1] = s[2];
d[2] = s[1];
d[3] = s[0];
return r;
}
int main() {
unsigned long l = 3206974079;
float f1 = (float &)l;
float f2 = ntohf(ntohf(f1));
unsigned char *c1 = (unsigned char *)&f1;
unsigned char *c2 = (unsigned char *)&f2;
printf("%02X %02X %02X %02X\n", c1[0], c1[1], c1[2], c1[3]);
printf("%02X %02X %02X %02X\n", c2[0], c2[1], c2[2], c2[3]);
getchar();
return 0;
}
The output is:
7F 8A 26 BF
7F CA 26 BF
I.e. the float assignment probably normalizes the value, producing a different value from the original.
Any input on this is welcomed.
EDIT2:
Thank you all for your replies. It seems the problem is that the swapped float, when returned via the 'return' statement, is pushed in the CPU's floating point stack. The caller then pops the value from the stack, the value is rounded, but it is the swapped float, and therefore the rounding messes up the value.
TCP tries to deliver unaltered bytes, but unless the machines have similar CPU-s and operating-systems, there's no guarantee that the floating-point representation on one system is identical to that on the other. You need a mechanism for ensuring this such as XDR or Google's protobuf.
You're sending binary data over the network, using implementation-defined padding for the struct layout, so this will only work if you're using the same hardware, OS and compiler for both application A and application B.
If that's ok, though, I can't see anything wrong with your code. One potential issue is that you're using ntohs to extract the length of the message and that length is the total length minus the header length, so you need to make sure you setting it properly. It needs to be done as
msg.length = htons(sizeof(ORIENTATION_MESSAGE) - sizeof(MESSAGE_HEADER));
but you don't show the code that sets up the message...