【问题标题】:Keeping a socket connection alive in iOS在 iOS 中保持套接字连接处于活动状态
【发布时间】:2012-07-08 09:49:24
【问题描述】:

我有以下用 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 分钟左右?)。我怎样才能保持这个连接活着?我知道存在断开连接,因为如果我在终端下连接到服务器,也会发生同样的事情,我必须保持该连接处于活动状态。我想我的代码中也发生了同样的事情。感谢您的帮助!

【问题讨论】:

标签: objective-c sockets ios5 timeout keep-alive


【解决方案1】:

您需要定期通过连接发送一些垃圾。试试这个:

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!"为您的协议提供任何您想要的东西。唯一的要求是远程端(套接字服务器)忽略该消息。

【讨论】:

  • 是否有设定的超时限制?我想知道我应该以什么频率发送垃圾数​​据。
  • @user:连接断开需要多长时间?在此之前安排一下。
  • 在某些情况下,您可能希望将其视为套接字服务器上的“心跳数据”,而不是“垃圾”。赞成! :)
  • @Linuxios : 什么是 sockfd?
  • @tonygil :我使用的是 URL 而不是 IP 地址。会不会有问题?还有,怎么解决?
【解决方案2】:

假设一个 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));
    }

我发布此内容是因为我花了几个小时才完成所有工作,希望为其他人节省精力。

【讨论】:

  • socket_handle, SOL_SOCKET, SO_KEEPALIVE 的值是多少?
  • man 2 setsockopt 或在 Xcode 文档视图中搜索 setsockopt。
  • 只有 Apple 才能做出如此简单、如此复杂的事情。我使用 Berkley Sockets 编写了联网应用程序,它比这更简单。
  • 这个方法需要在哪里写?
  • @HardikMamtora 在您获得对输出流的引用之后。
【解决方案3】:

我找到了如何做到这一点。由于服务器运行的是node.js,所以我使用了

socket.setKeepAlive(true, 2000);

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

【讨论】:

  • 您从 ios 流开始,现在您正在使用套接字对象?你在这里使用什么样的api?
  • @ItayLevin 他正在向您展示他是如何在服务器上做到这一点的 - 请参阅下面我的回答,了解如何在 iOS 上做到这一点。
【解决方案4】:

最简单的答案是尝试使用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 数据包,以便您可以检测到断开的连接。

【讨论】:

  • 嗯。有没有办法在 Objective-C 中使用上面的 API 来做到这一点?
  • @Hahnemann:我不太了解Objective-C,所以不知道。不过,我敢打赌,有一个逃生舱可以调用 C 库。
  • @Hahnemann:看看这里:developer.apple.com/library/ios/DOCUMENTATION/CoreFoundation/… 看看是否有帮助。
  • @JimR:我收到此错误消息:“未能设置 keepalive!ERRNO:非套接字上的套接字操作”可能是什么原因和可能的解决方案?
  • 嗨,getNativeSocketHandle() 是什么
猜你喜欢
  • 1970-01-01
  • 2018-04-13
  • 2013-09-23
  • 2012-01-03
  • 2021-09-13
  • 2013-08-27
  • 2019-09-01
  • 1970-01-01
  • 2021-07-31
相关资源
最近更新 更多