Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSInputStream does not call Delegate (stream:handleEvent:)

I searched the web for a long time...I didn't find an answer for my issue, so I decided to post here. I try to establish a connection to a NNTP-server using NSStream.

In a test-program, I open the streams and send a message. The delegate-method (stream:handleEvent:) is called twice for the output-stream (NSStreamEventOpenCompleted, NSStreamEventHasSpaceAvailable) but never for the input-stream!

Why does the input stream never call the delegate? Any ideas?

Basically, the code looks like this:

init and open streams:

CFReadStreamRef tmpiStream;
CFWriteStreamRef tmpoStream;

CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)SERVER, PORT, &tmpiStream, &tmpoStream);

iStream = (__bridge NSInputStream *) tmpiStream;
oStream = (__bridge NSOutputStream *)tmpoStream;

[iStream setDelegate:self];
[oStream setDelegate:self];

[iStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[oStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

[iStream open];
[oStream open];

send message:

NSData *data = [[NSData alloc] initWithData:[messageString dataUsingEncoding:NSASCIIStringEncoding]];
[oStream write:[data bytes] maxLength:[data length]];

receive messages:

-(void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
{
    NSLog(@"EventCode: %i", eventCode);
    //switch-case-statement...(using constants - NSStreamEventOpenCompleted...)
}

The class which contains that code inherits from NSObjects and implements NSStreamDelegate. (iOS5 with ARC)

Thx for any help!

EDIT: I just tried "polling" after opening streams like this - it's working:

while (![iStream hasBytesAvailable])
{}
uint8_t buffer[1024];
int len;
NSString *str = @"";

while ([iStream hasBytesAvailable]) 
{
    len = [iStream read:buffer maxLength:sizeof(buffer)];
    if (len > 0) 
    {
        NSString *output = [[NSString alloc] initWithBytes:buffer length:len encoding:NSISOLatin1StringEncoding];

        if (output != nil) 
        {
            str = [str stringByAppendingString:output];
        }
    }
}
NSLog(@"Response: %@", str);

But, for sure, I still need a better (async) solution ;)

like image 576
matrau Avatar asked Nov 13 '22 04:11

matrau


1 Answers

I found your answer here, I have the same issue. Your answer worked for me, but I think this works better:

How to use delegate in NSStream?

To quote him:

The run loop isn't running long enough for the delegate method to be called.

Add:

[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]];

right after you open the stream. This is only necessary in a program without a GUI -- otherwise the run loop would be spun for you.

So mine looks like this:

[self.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[self.inputStream open];
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:30.0]];

Edited: Then when you have the data, for example I have all my data complete when NSStreamEventEndEncountered occurs in the delegate method stream:handleEvent I put this:

[theStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
CFRunLoopStop(CFRunLoopGetCurrent());
[theStream close];

Which closes the run loop before the 30.0s or however long you set it. It would be the same to your answer if your code was in NSStreamHasBytesAvailable.

This allows me to still use the delegate methods and keep the run loop open until the download is completed, without skipping the delegate method all together.

like image 181
Diesel Avatar answered Dec 21 '22 07:12

Diesel