问题 在iOS中保持套接字连接存活


我有以下用Objective-C编写的代码,用于将数据写入套接字。服务器在Ubuntu上运行node.js:

NSString *url = @"anIPAddress";        
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;            
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)url, 9000, &readStream, &writeStream);
self.inputStream = (NSInputStream *)readStream;            
self.outputStream = (NSOutputStream *)writeStream;
[self.inputStream setDelegate:self];            
[self.outputStream setDelegate:self];            
[self.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];            
[self.outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];            
[self.inputStream open];            
[self.outputStream open];

我能够连接到服务器并发送信息。但是我注意到几分钟后连接超时(我想5分钟左右?)。我怎样才能保持这种联系?我知道有一个断开连接,因为如果我连接到终端下的服务器会发生同样的事情,我必须保持该连接存活。我想我的代码中也发生了同样的事情。谢谢你的帮助!


12467
2017-07-08 23:29


起源

对于有效的Objective-C代码,请参阅 以下回答... - Despotovic


答案:


最简单的答案是尝试使用 SO_KEEPALIVE 套接字选项。

如果没有数据在端点之间流动,则很难检测到断开的连接,这使得此选项在某些方面无用,因为它不使用数据来检测断开的连接。但是,很容易添加到您的代码中查看。添加它,看它是否有帮助......

这就是用C或C ++完成的

int opt = 1;
// Get the native socket handle from your socket class here...
int socket_handle = getNativeSocketHandle( self.outputStream );

/*Enabling keep alive*/
if( setsockopt( socket_handle, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof( opt ) ) < 0 )
{
   // failed setting socket option
}

不那么简单的答案是添加一个 ping 数据包到您的协议并定期发送ping数据包,以便您可以检测到断开的连接。


6
2017-07-08 23:58



嗯。有没有办法在Objective-C中使用上面的API? - Hahnemann
@Hahnemann:我不太了解Objective-C,所以我不知道。但是,我愿意打赌有一个逃脱舱口来调用C libs。 - JimR
@Hahnemann:看看这里: developer.apple.com/library/ios/DOCUMENTATION/CoreFoundation/... 并看看它是否有帮助。 - JimR
@JimR:我收到此错误消息:“未能设置keepalive!ERRNO:非套接字上的套接字操作”可能是什么原因和可能的解决方案? - Jayprakash Dubey
嗨,什么是getNativeSocketHandle() - Hardik Mamtora


我发现了如何做到这一点。由于服务器正在运行node.js,我使用了

socket.setKeepAlive(true, 2000);

以这种方式节点保持每个套接字打开。默认值为false,因此这就是超时的原因。我希望这对其他人有用。感谢您的回复,我认为保持心跳(保持活力)也是一个好主意,但是使用这种原生方法节点可以处理它。


3
2017-07-11 04:44



你从ios流开始,现在你正在使用套接字对象?你在这里使用什么样的api? - Itay Levin
@ItayLevin他正在向你展示他是如何在服务器上完成的 - 请参阅下面的答案,了解如何在iOS上进行操作。 - David H


假设有一个NSOutputStream * oStream;

在iOS上这样做:

#if 1 // Using CF
CFDataRef data = (CFDataRef)CFWriteStreamCopyProperty((__bridge CFWriteStreamRef)oStream, kCFStreamPropertySocketNativeHandle);
if(data) {
    CFSocketNativeHandle socket_handle = *(CFSocketNativeHandle *)CFDataGetBytePtr(data);
    CFRelease(data);
#else // Using Foundation
NSData *data = (NSData *)[oStream propertyForKey:(__bridge NSString *)kCFStreamPropertySocketNativeHandle];
if(data) {
    CFSocketNativeHandle socket_handle = *(CFSocketNativeHandle *)[data bytes];
#endif
    NSLog(@"SOCK HANDLE: %x", socket_handle);

    /*Enabling keep alive*/
    if( setsockopt( socket_handle, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof( opt ) ) < 0 )
    {
       NSLog(@"Yikes 2: failed to set keepalive! ERRNO: %s", strerror(errno));
    }

我发布这个因为我花了好几个小时全力以赴,想要为别人付出努力。


3
2018-02-10 23:32



socket_handle,SOL_SOCKET,SO_KEEPALIVE的值是什么? - Rohan
man 2 setsockopt或在Xcode文档视图中搜索setsockopt。 - David H
只有Apple才能制造如此简单,如此复杂的东西。我已经使用Berkley套接字对网络应用程序进行了编程,并且它没有那么复杂。 - deusprogrammer
哪里需要写这个方法? - Hardik Mamtora
@HardikMamtora刚刚获得对输出流的引用。 - David H


您需要定期在连接上发送一些垃圾。尝试这个:

NSTimer* t = [NSTimer scheduledTimerWithTimeInterval:240 target:self selector:@selector(timerMethod:) userInfo:[NSNumber numberWithInt:sockfd] repeats:YES];

然后宣布这个 self

-(void)timerMethod:(NSTimer*)t
{
  send([[t userInfo] intValue], "Keepalive!", 11, 0);
}

注意:替换“Keepalive!”随心所欲的协议。唯一的要求是远程端(套接字服务器)忽略该消息。


3
2017-07-09 00:05



是否有设定的超时限制?我想知道我应该以什么频率发送垃圾数​​据。 - iOS Calendar View OnMyProfile
@user:连接死了需要多长时间?在此之前安排一下。 - Linuxios
在某些情况下,您可能希望将其视为套接字服务器上的“心跳数据”,而不是“垃圾”。 UPVOTED! :) - tony gil
@tonygil:谢谢。 - Linuxios
@Linuxios:什么是sockfd? - Jayprakash Dubey