5

我在网上搜索了很长时间......我没有找到我的问题的答案,所以我决定在这里发布。我尝试使用 NSStream 建立与 NNTP 服务器的连接。

在测试程序中,我打开流并发送消息。委托方法 ( stream:handleEvent:) 为输出流 ( NSStreamEventOpenCompleted, NSStreamEventHasSpaceAvailable) 调用了两次,但从为输入流调用!

为什么输入流从不调用委托?有任何想法吗?

基本上,代码如下所示:

初始化和打开流:

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];

发信息:

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

接收消息:

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

包含该代码的类继承自 NSObjects 并实现NSStreamDelegate。(带有 ARC 的 iOS5)

感谢您的帮助!

编辑:我只是在打开这样的流后尝试“轮询” - 它正在工作:

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);

但是,可以肯定的是,我仍然需要一个更好的(异步)解决方案;)

4

3 回答 3

1

我在这里找到了你的答案,我也有同样的问题。你的回答对我有用,但我认为这更好:

如何在 NSStream 中使用委托?

引用他的话:

运行循环运行的时间不够长,无法调用委托方法。

添加:

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

在您打开流之后。这仅在没有 GUI 的程序中是必需的——否则将为您旋转运行循环。

所以我的看起来像这样:

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

编辑:然后当你有数据时,例如当委托方法流中发生 NSStreamEventEndEncountered 时我的所有数据都完成了:handleEvent 我把这个:

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

这会在 30.0 秒之前关闭运行循环,或者无论您设置多久。如果您的代码在 NSStreamHasBytesAvailable 中,那么您的答案将是相同的。

这允许我仍然使用委托方法并保持运行循环打开直到下载完成,而无需一起跳过委托方法。

于 2014-10-04T20:29:33.253 回答
0

我遇到了同样的问题,然后我意识到我不小心打了电话

[iStream setDelegate:self]

在我创建 iStream 之前。

于 2014-11-06T05:23:44.507 回答
0

您是否让运行循环以默认模式运行?

尝试检查输入流状态 ( -streamStatus)。

是否有任何内容记录到控制台?

您声称为输出流调用了委托方法,但您的NSLog()调用不会记录流。你确定吗?

__bridge_transfer最后,这是不相关的,但如果您打算将拥有的 Core Foundation 流对象的所有权转移到 ARC,则应该使用强制转换。更好的是,使用CFBridgingRelease()它与函数名称中的 Create 具有明显的对称性。除非有CFRelease()您没有显示的调用,否则现有代码会泄漏这些流。

于 2012-06-04T23:00:53.880 回答