cocoaasyncsocket sending data >128bytes (google protocol buffers) - objective-c

I'm using cocoaasyncsocket to send data Google Protocol Buffers (using http://code.google.com/p/metasyntactic/wiki/ProtocolBuffers) to a Java server. This is all fine BUT for messages (protoToSend) >128bytes I'm running into issues as the Java server can not read the message length correctly, I think because I'm sending the wrong length from Objective C.
I currently send the data as follows:
AsyncSocket *socket;
- (void)sendProtoToServer:(RequestMessage *)protoToSend {
NSData *d = [protoToSend data];
int s = [protoToSend serializedSize];
NSData *size = [NSData dataWithBytes:&s length:1];
[socket writeData:size withTimeout:TIME_OUT tag:100];
[socket writeData:d withTimeout:TIME_OUT tag:101];
}
Any ideas?
Thanks in advance

The length is little-endian varint encoded, presumably - meaning it is in chunks of 7-bits with the MSB as a continuation bit. If the MSB is set, then you need to process the next byte (and so on) to get the combined length, then use bitwise shift to combine them.
Indeed, for all numbers < 128, this indeed looks identical to reading a single byte.
See here for the spec on decoding base-128 varints.

Related

Objective-C/Cocoa equivalent of fread

I'm trying to read in the first four bytes of a file. I know that this works correctly with the following C code:
FILE *file = fopen(URL.path.UTF8String, "rb");
uint data;
fread(&data, 4, 1, file);
NSLog(#"%u", data);
This prints out: 205
I'm trying to find the equivalent way of doing this in Objective-C/with Cocoa functions. I've tried a number of things. I feel like the following is close:
NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingFromURL:URL error:nil];
NSData *data2 = [fileHandle readDataOfLength:4];
NSLog(#"%#", data2);
NSLog(#"%u", (uint)data2.bytes);
This prints out: < cd000000 >
and: 1703552
As expected, the first four bytes of the file are indeed CD000000.
I'm assuming there's one of two things causing the difference (or both):
fread is not counting the 0s following the CD. I've confirmed this by only reading in 1 byte with the fileHandle, but sometimes this number will extend greater than one byte, so I can't restrict it like this. Do I need to manually check that the bytes coming in aren't 00?
This has something to do with endianness. I have tried a number of functions such as CFSwapInt32BigToHost but have not been able to get back the right value. It would be great if anyone can enlighten me as to how endianness works/effects this.
You are not dereferencing the data.
NSLog(#"%u", (uint)data2.bytes); // wrong
The "quick hack" version is like this:
NSLog(#"%u", *(uint *) data2.bytes); // hack
A more robust solution requires copying to a variable somewhere, to get the alignment right, but this doesn't matter on all platforms:
uint value;
[data getBytes:&value length:sizeof(value)];
NSLog(#"%u", value);
Another solution is to explicitly read the data byte-by-byte, which is most portable, has no alignment issues on any platform, and has no byte-order issues on any platform:
unsigned char *p = data.bytes;
uint value = (unsigned) p[0] | ((unsigned) p[1] << 8) |
((unsigned) p[2] << 16) | ((unsigned) p[3] << 24);
NSLog(#"%u", value);
As you can see, there are good reasons why we avoid putting binary data in files ourselves, and leave it to libraries or use text formats.
This can't be an issue with byte order, because fread() is working correctly. The fread() function and the -readDataOfLength: method will both give you the same result: a chunk of bytes.
You attempt reinterpret a sequence of 4 bytes as an unsigned int. This is not guaranteed to work on all platforms. It will only work if sizeof(unsigned int) equals 4. And it will only work if the byte order is the same for reading and writing.
Furthermore, you are not printing the scalars correctly with NSLog.
fread() in binary mode won't do anything to your data, you'll get the bytes as they are in the file.
It's absolutely byte ordering that is causing this, but I don't know anything about Apple's Objective C API:s. I don't understand why you don't need to do pointer accesses to the data2 object, even (why isn't data2.bytes failing, and data2->bytes needed?).
Also, the documentation for NSData doesn't say anything about byte order that I could find.

Sending packets to Minecraft server: Objective c

I've been trying to send packets to a minecraft server from my custom Cocoa application (written in objective-c of course). I am a little confused as how to do that though. I did it in Java. That was very easy. Doing this is objective-c though is proving to be a bit more challenging.
This is the code that I am using:
- (void)handshake
{
PacketHandshake *packet = [PacketHandshake packetString:[NSString stringWithFormat:#"%#;%#:%i", username, IP, PORT]];
[packet writeData:dataOut];
}
Which calls:
- (void)writeData:(NSOutputStream *)dataOut
{
[super writeData:dataOut]; //Writes the "header" which is a char with the value of 0x02 (char packetID = 0x02)
NSUInteger len = [string lengthOfBytesUsingEncoding:NSUTF16BigEndianStringEncoding]; //Getting the length of the string i guess?
NSData *data = [string dataUsingEncoding:NSUTF16BigEndianStringEncoding]; //Getting string bytes?
[dataOut write:(uint8_t*)len maxLength:2]; //Send the length?
[dataOut write:[data bytes] maxLength:[data length]]; //Send the actual string?
}
I have established a successful connection to the server beforehand, but I don't really know whether or not I am sending the packets correctly. Could somebody please explain how I should send various data types and objects. (int, byte/char, short, double, NSString, BOOL/bool)
Also, is there any specific or universal way to send packets like the ones required by Minecraft?
Ok, I guess the question is now: how do data types, mainly strings, relate in Java and Objective-C?
Any help is appreciated, thank you!
Nobody knows?
Maybe you're running into a network/host byte order problem? I know very little about Minecraft- but I note that it's mentioned here that shorts in the Minecraft protocol use network byte order, which is big-endian (all other data types are 1 byte long so endianness is not relevant).
All x86 machines use little-endian.
I don't know whether your PacketHandshake class is converting the data before sending it- if not you could use the c library functions ntohs() and htons(), for which you'd need to include sys/types.h and
netinet/in.h
The link also mentions that strings are 64 byte array of standard ASCII chars, padded with 0x20s. You can get the ASCII value out of an NSString by doing [string UTF8String], which returns const char*- i.e. your standard C String ending with a 0x0, and then maybe pad it. But if it just works in Java, then maybe you don't need to.

Parse Midi Packet in iOS

I'm having a hard time parsing Midi Packets. At times its 3 bytes then it can be 155 bytes on one stream.
How can I iterate through the massive packet and just get what I need?
Say for "b0" its only 3 bytes of 12 a byte packet, I just want to split "b0" and its following bytes:
[b02c2c] then the others [b02c2d] or [f0....] in the same packet...
Heres what I've been working on and is giving me a headache..
NSString *StringFromPacket(const MIDIPacket *packet,id self)
{
NSMutableString * result = [[NSMutableString alloc] init];
for (int i = 0; i < packet->length; i++)
{
NSString *s = [NSString stringWithFormat:#"%02x",packet->data[i]];
for (NSString *line in [s componentsSeparatedByString:#"b0"])
{
// This appends to string but b0 disappears and only get the following 2 bytes
// Along with the others like f0,a0,90. I would like to filter without losing b0
[result appendFormat:line];
}
}
[self controlEvent:result];
}
-(void)controlEvent:(NSString *)line
{
if (line == #"b02c2c")
{
//Do Something
}
}
I have no experience with IPhone programming, but I had quick look at iOS MIDI API.
In MIDI there are various MIDI events (note on, note off, controller change, etc.) Each event can have different number of bytes. From first byte you can event type and from the type you know message length (see MIDI reference at http://www.midi.org/techspecs/midimessages.php ) The only exception is System Exclusive MIDI message (for more details see MIDi reference and reference for specific device that can send and receive System Exclusive messages)
It seems that MIDIPacket can contain variable number of MIDI messages. So you can have look at first byte. Let's suppose you received a Note On midi event on MIDI channel 1. In first byte you will have value 80 (hex). From documentation you can see that Note On Event has two data bytes. Next byte is MIDI note number (you can see list of MIDI note numbers e.g. on http://midikits.net23.net/midi_analyser/midi_note_numbers_for_octaves.htm ). And the next note number is velocity (how fast (or hardly) was key pressed). And then you can repeat this procedure.
Note that for some messages, like Pitch Wheel Change, two data bytes carry one value (MSB and LSB).
It might be useful to you to know that when the byte starts with 0, it is MIDI data byte. MIDI events have 1 in their first byte.
Another useful resource: http://home.roadrunner.com/~jgglatt
Specifically, you might want to check out the section on the MIDI protocol messages: http://home.roadrunner.com/~jgglatt/tech/midispec.htm
Beyond that, parsing MIDI is a matter of just walking through the bytes and handling the messages accordingly.

Objective-C: Best way to package the contents of a file for Bluetooth transfer?

In my program I am sending a file through an RFCOMM connection with a paired bluetooth device. I am having a problem with the receiving end writing the received bytes to a file. However, the receiving end may not be the root of the problem. It may be the way I am loading and sending the data intially in Objective-C. So, my question here is whether or not my method of loading and transmitting the file's data as bytes across an RFCOMM connection is acceptable. I am trying to avoid posting a lot of code because it will truly just get in the way. I would just like to know if it is ok or not to load file data into an array as I have for transferring, or if there is a better way to package the data?
Thanks in advance.
Data Types Involved
IOBluetoothRFCOMMChannel channel
NSData myData
Load File Code Snippet
// Load file data into byte array.
myData = [NSData dataWithContentsOfFile: filePath];
int bytes[(int)fileSize];
[myData getBytes:bytes length:sizeof(int) * fileSize];
Transmit File Data Code Snippet
// Loops create an int[] chunk array that holds an amount of data from bytes[].
// These chunks (packets) are sent using...
[channel writeSync: chunk length: 1000]; // Sends the packet.
You need to use the Archives or Serialization methods to convert the bytes streams into an architecture independent bytestream and then send over the RFCOMM channel, and at the receiving end convert it back
See Here

Objective-C Packing Data using NSMutableData?

I need to put a short and integer at the begging of a message that i am sending to a java server. The server is expecting to read a short (message id) then an integer (message length). I've read in the stackoverflow that NSMutableData is similar to java ByteBuffer.
I am trying to pack the message into NSMutableData then send it.
So this is what I have but is not working !.
NSMutableData *data = [NSMutableData dataWithLength:(sizeof(short) + sizeof(int))];
short msg_id = 2;
int length = 198;
[data appendBytes:&msg_id length:sizeof(short)];
[data appendBytes:&length length:sizeof(int)];
send(sock, data, 6, 0);
The server is using Java ByteBuffer to read in the received data. So the bytes coming in is:
32,120,31,0,2,0
which is invalid.
The correct value so the ByteBuffer can read them as .getShort() and .getInt()
0,2,0,0,0,-66
You're basically putting stuff into the NSData object correctly, but you're not using it with the send function correctly. First off, as dreamlax suggests, use NSMutableData's -initWithCapacity initializer to get a capacity, not zeroed bytes.
Your data pointer is a pointer to an Objective-C (NSData) object, not a the actual raw byte buffer. The send function is a classic UNIX-y C function, and doesn't know anything about Objective-C objects. It expects a pointer to the actual bytes:
send(sock, [data bytes], [data length], 0);
Also, FWIW, note that endianness matters here if you're expecting to recover the multibyte fields on the server. Consider using HTONL and HTONS on the short and int values before putting them in the NSData buffer, assuming the server expects "network" byte order for its packet format (though maybe you control that).
I think your use of dataWithLength: will give you an NSMutableData object with 6 bytes all initialised to 0, but then you append 6 more bytes with actual values (so you'll end up with 12 bytes all up). I'm assuming here that short is 2 bytes and int is 4. I believe you want to use dataWithCapacity: to hint how much memory to reserve for your data that you are packing.
As quixoto has pointed out, you need to use the bytes method, which returns a pointer to the first byte of the actual data. The length method will return the number of bytes you have.
Another thing you need to watch out for is endianness. The position of the most significant byte is dependent on the underlying architecture.