我在网上搜索了很长时间......我没有找到问题的答案,所以我决定在这里发帖。 我尝试使用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);
但是,当然,我仍然需要一个更好的(异步)解决方案;)
答案 0 :(得分:0)
您是否让运行循环以默认模式运行?
尝试检查输入流状态(-streamStatus
)。
是否有任何记录到控制台?
您声明为输出流调用了委托方法,但您的NSLog()
调用未记录该流。你确定吗?
最后,这是不相关的,但如果您要将拥有的Core Foundation流对象的所有权转移到ARC,则应使用__bridge_transfer
强制转换。更好的是,使用CFBridgingRelease()
,因为它与函数名称中的Create具有明显的对称性。除非您没有显示CFRelease()
次调用,否则现有代码会泄漏这些流。
答案 1 :(得分:0)
我在这里找到你的答案,我有同样的问题。你的回答对我有用,但我觉得这样做更好:
How to use delegate in 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.0s之前关闭运行循环或者设置它的时间长。如果你的代码在NSStreamHasBytesAvailable中,那么你的答案就会一样。
这允许我仍然使用委托方法并保持运行循环打开直到下载完成,而不是一起跳过委托方法。
答案 2 :(得分:0)
我有同样的问题然后我意识到我不小心打电话了
[iStream setDelegate:self]
在我创建iStream之前。