# Reading and Parsing JSON from a Game Server

This is my naive (but currently working) attempt at reading JSON commands from a Java game server in Objective-C. I am afraid I might be doing something wrong, so hopefully this question will be a learning experience for me.

To give a little background, someone else built the game server in Java in order to communicate with a Java client. All communications sent and received are JSON commands formatted like {"command":"nameOfCommand","message":"messageText"}. I realized that it would be easily possible to interface with this server from whatever platform I want. The only technical hurdle to overcome is parsing the JSON strings and turning them into useable objects.

Here is the code that I use to connect to the server. I found this code on Stack Overflow but I believe it is boilerplate code from a reference manual. I have not really changed it much, but it should be useful to understand why I do things the way I do them.

-(BOOL) initializeConnection {
CFWriteStreamRef writeStream;

NSOutputStream *outputStream = (NSOutputStream *)CFBridgingRelease(writeStream);
[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];
[outputStream open];

if (inputStream) {
[self sendInitialGameMessage];
return YES;
} else {
return NO;
}
}


Then I have this method which actually processes the sending and receiving of data from the server:

- (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent {

switch (streamEvent) {

case NSStreamEventHasBytesAvailable: {
uint8_t *buffer;
NSUInteger length;

NSData *data = [NSData dataWithBytes:buffer length:length];
NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];

[_jsonParser acceptString:string];

#if DEBUG == 1
}
#endif

break;
}

case NSStreamEventHasSpaceAvailable: {
if (_dataToSend != nil) {
[(NSOutputStream *)theStream write:[_dataToSend bytes] maxLength:[_dataToSend length]];
_dataToSend = nil;
}
break;
}

default:
break;
}
}


Here is the header file of my parser:

#import <Foundation/Foundation.h>

@interface CSCJsonParser : NSObject

-(void) acceptString:(NSString *)string;

@end


And here is the code that I need reviewed the most, the implementation:

#import "CSCJsonParser.h"

@implementation CSCJsonParser {
NSString *_currentJson;
}

-(instancetype) init {
self = [super init];
if (self) {
}
return self;
}

}

-(void) acceptString:(NSString *)string {
if (!_currentJson) {
_currentJson = @"";
}

_currentJson = [_currentJson stringByAppendingString:string];

if ([string isEqual: @"}"]) {
_currentJson = nil;
}
}

-(NSDictionary *) jsonDataForString:(NSString *)string {
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
NSError *error = nil;
NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];

if (error) {
return nil;
} else {
return dictionary;
}
}

@end


I know this is a naive implementation, but is it too simple? Is there enough error checking? The basic premise is that server commands will always end with }, so that is where the parser divides the strings.

I tried to directly read the stream with JSON, but I could not get this to work. Every time the switch enters the NSStreamEventHasBytesAvailable case, a single string is received by the server. This is why I grab all of the strings for the JSON and then pass it to NSJSONSerialization to turn it into a dictionary. I should note that I also looked into a couple libraries to do this, but they seemed very heavyweight for what I am trying to accomplish.

This method does work, and it gives me a correct dictionary with the expected keys and values. My next step is to extend the program to be able to parse the dictionary and create an appropriate Message that can be used by the Client. I will also be making something that can do the opposite to correctly create and format a Message to send to the server. However, I wanted to post this question first to see if my approach so far is a valid one.

• "someone else" = me :) – Simon Forsberg Sep 24 '14 at 17:38
• This is a "valid" approach, but it certainly has room for improvement. The first question I have for you... 127.0.0.1 is obviously a placeholder address, and one way or the other, won't be in the final product, correct? Is the final code intended to make a TCP/IP connection to something on the local network, or would the final code connect to a server on the Internet using an address like http://example.com/myserver? – nhgrif Sep 25 '14 at 1:04
• In my testing it is connecting to a local Java server. In the final version it will not connect to an http but rather directly to an ip address and port. The server is not set up to handle http. – bazola Sep 25 '14 at 1:11

A good starting point for any question regarding Objective-C will always be the official Apple documentation on the subject, and in this case, more specifically, we want this page: Reading from Input Streams.

There are a few things about reading from input streams that you miss here.

First, we shouldn't be spending time converting data to strings and appending to JSON string, etc., in the midst of an open read stream. We should read from our stream as quickly as the stream provides it and close the stream.

There's another case in your switch that is useful: NSStreamEventEndEncountered. Also, we of course need NSStreamEventErrorOccurred.

But the biggest thing we're missing is defining an actual max length we want to read out of the input stream and checking the actual length read.

case NSStreamEventHasBytesAvailable: {
uint8_t *buffer;
NSUInteger length;

NSData *data = [NSData dataWithBytes:buffer length:length];
NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];

[_jsonParser acceptString:string];

#if DEBUG == 1
}
#endif

break;
}


We should instead write it a bit more like this...

First, declare an NSMutableData property, which for this example will be called jsonData. Now, make our HasBytesAvailable look like this:

case NSStreamHasBytesAvailable: {
if (!self.jsonData) {
self.jsonData = [NSMutableData data];
}

static int const BUFFER_LENGTH = 1024; // read 1kb at a time
uint8_t buffer[BUFFER_LENGTH];
NSInteger length = 0;

length = [(NSInputStream *)theStream read:buffer maxLength:BUFFER_LENGTH];

if (length > 0) {
[self.jsonData appendBytes:(const void *)buffer length:length];
if (length < BUFFER_LENGTH) {
// there will be nothing left in the buffer
// we can now convert self.jsonData directly into a dictionary
// with some NSJSONSerialization probably
}
} else {
// there was probably an error reading from the buffer
// probably have to start this whole process over
// although I think it's not wholly impossible to get 0 as a valid read
// a negative number is definitely invalid data, which is why we
// must use NSInteger for length rather than NSUInteger
}
}


Now we spend 100% of our execution time while the stream is open just dealing with the stream and the data it's giving us.

The value of BUFFER_LENGTH doesn't have to be 1kb. If you know the maximum size of the data you'll ever receive from this particular request, you can make this value just slightly larger than that (so that if (length < BUFFER_LENGTH) returns true the first time every time).

Also, for what it's worth, you can transfer a lot more data in a significantly smaller package if you don't use JSON or XML or any sort of format like this. These formats are largely designed for either a) web services where you'll have a large public audience and you want to make consuming the webservice as easy as possible or b) dealing with things like .plist (just as an example) where you're keeping a file on hand that you want to be able to easily edit with a plain text editor but still want to transfer around.

But if you're developing the server yourself, or working hand-in-hand with the server developer, it would be far better to agree on how to serialize the data and just pass it as plain bytes. Use 1-4 bytes at the front as a header to describe some meta data about what you're passing, and use the rest of the bytes as the plain data.

Consider if I want to ask the server for the values of foo, bar, and baz. Let's say these values will always be in the range of 0-255, a uint8 value. Consider that as JSON, it will look like this:

{foo=115,bar=205,baz=155}


That's as condensed as you can make it. And I don't know whether or not that's even valid JSON... I'm not sure on the white space requirements, but even best case scenario, that's sent as plain text. It's 1 byte per character. There are 25 characters. So the total package is 25 bytes... but we're only sending 3 bytes of data.

Instead, we could just send the data as data. If we know that we're always going to get foo, bar, and baz, we don't need them to be labeled.

So why don't we just send this 4-byte package? The first byte is a very simple header that tells us how long the total package is (you and the server developer have to agree on what sort of information, if any, you need to put in the header). The remaining three bytes are the values of foo, bar, and baz. In hex, the 4-byte package would look like this:

04 73 CD 9B


So, there's 4 bytes to transfer 3 bytes worth of data, versus JSON using 25 bytes to transfer 3 bytes worth of data.