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 {
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)@"127.0.0.1", 4242, &readStream, &writeStream);
NSInputStream *inputStream = (NSInputStream *)CFBridgingRelease(readStream);
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;
[(NSInputStream *)theStream read:buffer maxLength:length];
NSData *data = [NSData dataWithBytes:buffer length:length];
NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
[_jsonParser acceptString:string];
#if DEBUG == 1
if ([_jonParser lastReceivedJson]) {
NSLog(@"%@", [_jonParser lastReceivedJson]);
}
#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
@property (readonly) NSMutableArray *receivedJsons;
-(NSDictionary *)lastReceivedJson;
-(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) {
_receivedJsons = [[NSMutableArray alloc]init];
}
return self;
}
-(NSDictionary *)lastReceivedJson {
return [_receivedJsons lastObject];
}
-(void) acceptString:(NSString *)string {
if (!_currentJson) {
_currentJson = @"";
}
_currentJson = [_currentJson stringByAppendingString:string];
if ([string isEqual: @"}"]) {
[_receivedJsons addObject:[self jsonDataForString:_currentJson]];
_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.
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 likehttp://example.com/myserver
? \$\endgroup\$