简体   繁体   中英

hasBytesAvailable finishing early

I am sending JSON from my server to my cocoa app.

The cocoa app receives data like this:

case NSStreamEventHasBytesAvailable:
    NSLog(@"begin");
    if (theStream == inputStream) {
        int len;
        uint8_t buffer[4096];
        NSString* incoming_message = @"";
        while ([inputStream hasBytesAvailable]) {
            len = (int)[inputStream read:buffer maxLength:sizeof(buffer)];
            NSLog(@"\nThe length is -- %d\n",len);
            if (len > 0) {
                NSString *mess = [[NSString alloc] initWithBytes:buffer length:len encoding:NSASCIIStringEncoding];
                if(mess != nil){
                    incoming_message = [NSString stringWithFormat:@"%@%@",incoming_message,mess];
                }
            }
        }

        NSLog(@"%@",incoming_message);

        // handle icoming message
        [self handleIncomingNotification:incoming_message];
    }
    NSLog(@"end");

    break;

The problem is, is if the data begins to be any larger than something like 500 characters it splits into two (the begin and end log are called twice) so handleIncomingNotification: does not work properly as it is passing half the JSON.

When I log len I get 1448 a lot and sometimes 1479 and then a one off of 4096

So obviously hasBytesAvailable is not working like it should.

I have tried changing the buffer but that makes no difference.

Please help me come up with a way to fix this. I am surprised this was not an easy google.


Edit from xaphod's answer

I have ended up doing this:

case NSStreamEventHasBytesAvailable:
    NSLog(@"begin");
    if (theStream == inputStream) {
        int len;
        uint8_t buffer[1024];
        NSString* incoming_message = @"";
        while ([inputStream hasBytesAvailable]) {
            len = (int)[inputStream read:buffer maxLength:sizeof(buffer)];
            if (len > 0) {
                NSString *mess = [[NSString alloc] initWithBytes:buffer length:len encoding:NSASCIIStringEncoding];
                    incoming_message = [NSString stringWithFormat:@"%@%@",incoming_message,mess];
            }
        }

        [self handleStreamMessage:incoming_message];
    }
    NSLog(@"end");

    break;

handleStreamMessage:

NSString*split_message;
-(void)handleStreamMessage:(NSString*)message{
    if(split_message == NULL){
        split_message = @"";
    }else if([split_message length] > 0){
        message = [NSString stringWithFormat:@"%@%@",split_message,message];
    }
    NSLog(@"message:%@",message);
    NSError *error;
    NSData* data = [message dataUsingEncoding:NSUTF8StringEncoding];
    if ([NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error] == nil)
    {
        // not finished the stream as not given proper json data
        split_message = message;
    }else{
        [self handleIncomingNotification:message];
        split_message = @"";
    }
}

Which appears to be an awful hack that works.

That is not how NSStream networking streams work. If you send X bytes, the receiver will receive N times some amount, which in total will (eventually) be X. N might be 1, 7, or 42 - depends on multiple things.

What you need to do is parse the JSON as it comes in and determine when you have finished receiving enough that it is meaningful. Check out YAJLParser, i use it to do exactly this with the twitter api. Let me get you a link...

.. my fork is here: https://github.com/xaphod/yajl-objc

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM