NSStream TCP Keep-alive iOS
我已经写了这个代码来设置一个服务器的stream:
-(void)streamOpenWithIp:(NSString *)ip withPortNumber:(int)portNumber; { CFReadStreamRef readStream; CFWriteStreamRef writeStream; CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (__bridge CFStringRef)ip, portNumber, &readStream, &writeStream); if(readStream && writeStream) { CFReadStreamSetProperty(readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue); CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue); //Setup inpustream inputStream = (__bridge NSInputStream *)readStream; [inputStream setDelegate:self]; [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; [inputStream open]; //Setup outputstream outputStream = (__bridge NSOutputStream *)writeStream; [outputStream setDelegate:self]; [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; [outputStream open]; } }
我可以连接到服务器并发送和接收数据。 但是我想检查连接是否仍然存在。 如果我从无线路由器断开连接线,我仍然可以发送stream中的数据,没有发生错误。
你可以通过使用计时器来发送一些消息并检查是否收到回复,从而在应用程序级别上解决这个问题。 但是,您也可以在较低级别上使用TCP Keep-Alive技术来解决此问题。
我如何用NSStream实现这个? 我怎样才能设置检查间隔?
我假设你通过TCP Keep-Alive来检查stream时是否收到NSStreamEventErrorOcurred?
我已经检查了这个post,但我无法弄清楚: 在iOS中保持套接字连接
谢谢你的帮助!
你可以得到原生的套接字句柄
CFDataRef socketData = CFReadStreamCopyProperty((__bridge CFReadStreamRef)(inputStream), kCFStreamPropertySocketNativeHandle); CFSocketNativeHandle socket; CFDataGetBytes(socketData, CFRangeMake(0, sizeof(CFSocketNativeHandle)), (UInt8 *)&socket); CFRelease(socketData);
然后设置套接字选项(你需要#include <sys/socket.h>
):
int on = 1; if (setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) == -1) { NSLog(@"setsockopt failed: %s", strerror(errno)); }
你可以把这段代码放在kCFStreamEventOpenCompleted
事件的事件处理函数中:
- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)event { switch (event) { case kCFStreamEventOpenCompleted: if (stream == self.inputStream) { CFDataRef socketData = CFReadStreamCopyProperty((__bridge CFReadStreamRef)(stream), kCFStreamPropertySocketNativeHandle); CFSocketNativeHandle socket; CFDataGetBytes(socketData, CFRangeMake(0, sizeof(CFSocketNativeHandle)), (UInt8 *)&socket); CFRelease(socketData); int on = 1; if (setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) == -1) { NSLog(@"setsockopt failed: %s", strerror(errno)); } } break; /* ... other cases ... */; } }
这个委托方法给出连接的stream状态。 你可以在这里检查你的连接状态
-(void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)event { NSLog(@"Stream triggered."); switch(event) { case (NSStreamStatus)NSStreamEventErrorOccurred: { NSLog(@"%@",[stream streamError].localizedDescription); } break; case NSStreamEventHasSpaceAvailable: { if(stream == outputStream) { NSLog(@"outputStream is ready."); } break; } case NSStreamEventHasBytesAvailable: { if(stream == inputStream) { NSLog(@"data can be read here!"); } } break; } default: { NSLog(@"Stream is sending an Event: %i", event); break; } } }
有一个类似的问题更完整的答案。
对于10秒后开始发送Keepalive的应用程序示例,每隔2秒发送一次keppalive,并在4个keepalive之后closuresstream,不回复,看看这篇文章: 是否可以在Apple iOS设备上激活TCP keepalive
它还显示了如何设置连接closures后的重新传输超时。