0
votes

I've got an app that receives JSON messages over TCP. This works perfectly for smaller messages, however sometimes when the messages are large they get cut off in the middle, and the app is unable to parse them.

My iOS code:

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

    collectedData = [NSMutableData data];

    switch (streamEvent) {

        case NSStreamEventOpenCompleted:
            NSLog(@"Stream opened");

            break;
        case NSStreamEventHasBytesAvailable:

            if (theStream == inputStream) {


                NSData *nl = [@"\n" dataUsingEncoding:NSUTF8StringEncoding];

                uint8_t bufferz[1024];
                int lenz;
                while ([inputStream hasBytesAvailable]) {
                    lenz = [inputStream read:bufferz maxLength:sizeof(bufferz)];
                    [collectedData appendBytes: (const void *)bufferz length:lenz];
                }
                NSRange nlRange =[collectedData rangeOfData:nl options:0 range:NSMakeRange(0, [collectedData length])];
                while (nlRange.location != NSNotFound) {
                    // Extract data from the beginning up to (but not including) the newline character:
                    NSData *jsonData = [collectedData subdataWithRange:NSMakeRange(0, nlRange.location)];
                    // Remove data from the beginning up to and including the newline character:
                    [collectedData replaceBytesInRange:NSMakeRange(0, nlRange.location + nlRange.length) withBytes:NULL length:0];

                    // Process jsonData ...
                    NSError *error;
                    NSMutableDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&error];
                    // ...


                    NSString* type = [jsonDict objectForKey:@"type"];

                    //NSLog(@"NEW NEW NEW --> %@\n", jsonDict);
                    NSString *outputData = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];


                    if(error) {
                        NSLog(@"THE STRING ------------>>>>> %@\n", outputData);
                        NSLog(@"PARSE ERROR ------------->>>>> : %@\n", error);
                    }


                    //NSLog(@"TYPEN ----------------------------------------------> %@", type);

                    if([type isEqualToString:@"message"]) {
                        //NSLog(@"New chat message: %@", output);

                        [self messageReceived:outputData];

                    } else if([type isEqualToString:@"offlineMessages"]) {
                        NSLog(@"New offline messages: %@", outputData);
                        NSLog(@"NEW OFFLINE MESSAGES!!");
                        [self offlineMessagesReceived:outputData];

                    }

                    // Check for another newline character:
                    nlRange =[collectedData rangeOfData:nl options:0 range:NSMakeRange(0, [collectedData length])];
                }
            }
            break;


        case NSStreamEventErrorOccurred:

            [self closeNetworkCommunication];
            isConnected = 0;

            NSLog(@"STREAM ERROR");
            //theStream = nil;

            break;

        case NSStreamEventEndEncountered:

            [self closeNetworkCommunication];
            isConnected = 0;

            NSLog(@"STREAM END");
            //theStream = nil;

            break;
    }
}

This is an example string that the server is sending the app:

{"id":"1", "type":"offlineMessages", "msg": "{\"agentsArray\":[{\"id\":281102,\"msgFrom\":\"V281570150514504\",\"msgDate\":\"2014-04-10T11:09:49.000Z\",\"msgBody\":\"Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec odio tellus, dictum eu congue id, malesuada non risus. Mauris dapibus velit massa, non laoreet lectus lacinia nec. Morbi sagittis molestie eros, nec blandit sem accumsan eget. Aliquam eu sem egestas, malesuada urna sit amet, gravida velit. Curabitur auctor sit amet turpis vitae feugiat. Nam id velit viverra eros blandit consectetur vel eu tortor. Nullam at augue eros. Praesent dolor libero, venenatis in adipiscing vitae, dignissim nec nisi. In lobortis sem a neque ultrices, a vestibulum urna ullamcorper. Pellentesque eu suscipit est, quis dignissim orci. Aenean condimentum vitae dolor ac cursus. Aenean eu pulvinar nunc, et mollis erat. Vestibulum commodo malesuada nunc et iaculis. In mattis cursus mi vitae gravida. Etiam et iaculis tellus, non iaculis nulla.\",\"channelID\":\"V281570150514504\",\"recipient\":\"[email protected]\"}]}", "name": "281", "time": "12:09", "channel": "V281570150514504"}\n

Sometimes the app will parse the message like this:

auctor sit amet turpis vitae feugiat. Nam id velit viverra eros blandit consectetur vel eu tortor. Nullam at augue eros. Praesent dolor libero, venenatis in adipiscing vitae, dignissim nec nisi. In lobortis sem a neque ultrices, a vestibulum urna ullamcorper. Pellentesque eu suscipit est, quis dignissim orci. Aenean condimentum vitae dolor ac cursus. Aenean eu pulvinar nunc, et mollis erat. Vestibulum commodo malesuada nunc et iaculis. In mattis cursus mi vitae gravida. Etiam et iaculis tellus, non iaculis nulla.\",\"channelID\":\"V281570150514504\",\"recipient\":\"[email protected]\"}]}", "name": "281", "time": "12:09", "channel": "V281570150514504"}\n

I should add that this does not happend all the time, its around 50% off the times.

Any ideas what I'm doing wrong?

2
Try to append the data until you receive the end of the message and try to parse it only then.Christian Schnorr
@Jenox How would i accomplish that?Alosyius
What exactly are you trying to accomplish with the newline stuff?Christian Schnorr
Im using the newline to separate messagesAlosyius
You might give JSONObjectWithStream:options:error: a try. I've never tested this myself where the input are multiple JSON documents, though.CouchDeveloper

2 Answers

1
votes

It sounds like you're probably running into issues around TCP packet sizes. So when a message is large, it's split up and not received all at once.

From the way it looks, you would need to build in checks to make sure you have all of the bytes you are expecting before attempting to process the JSON.

Here is the Apple documentation for something kind of "similar", https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Streams/Articles/ReadingInputStreams.html#//apple_ref/doc/uid/20002273-BCIJHAGD.

You will see from that it can handle multiple runs of handleEvent and appends the data to the buffer as it goes.

Hope that helps.

-1
votes

Use following for getting JSON data from server

 NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:myUrl completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {

    NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
    NSLog(@"%@", json);
}];
[dataTask resume];