是否有可能在Apple iOS设备上激活TCP Keepalive

苹果设备===路由器=== WiFi模块

苹果设备(iPhone)正在通过TCP连接连接到WiFi模块端口2000。 我想在Apple设备上激活TCP keepalive包发送,以确定什么时候TCP连接到WiFi模块丢失(模块closures)。

我的stream设置

CFReadStreamRef readStream; CFWriteStreamRef writeStream; CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)CFBridgingRetain(moduleIPaddress), port2000, &readStream, &writeStream); outputStream = (NSOutputStream *)CFBridgingRelease(writeStream); inputStream = (NSInputStream *)CFBridgingRelease(readStream); [outputStream setDelegate:(id)self]; [inputStream setDelegate:(id)self]; [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; [outputStream open]; [inputStream open]; 

我试图激活keepalive根据大卫H后保持套接字连接在iOS中活着

 - (void) stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent { switch (streamEvent) { case NSStreamEventOpenCompleted: if (theStream == outputStream) { /* CFDataRef data = (CFDataRef)CFWriteStreamCopyProperty((__bridge CFWriteStreamRef)theStream, kCFStreamPropertySocketNativeHandle); if(data) { CFSocketNativeHandle socket_handle = *(CFSocketNativeHandle *)CFDataGetBytePtr(data); CFRelease(data); NSLog(@"SOCK HANDLE: %x", socket_handle); //Enabling keep alive int opt = 1; if( setsockopt( socket_handle, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof( opt ) ) < 0 ) { NSLog(@"Yikes 2: failed to set keepalive! ERRNO: %s", strerror(errno)); } } */ NSData *data = (NSData *)[theStream propertyForKey:(__bridge NSString *)kCFStreamPropertySocketNativeHandle]; if(data) { CFSocketNativeHandle socket_handle = *(CFSocketNativeHandle *)[data bytes]; NSLog(@"SOCK HANDLE: %x", socket_handle); //Enabling keep alive int opt = 1; if( setsockopt( socket_handle, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof( opt ) ) < 0 ) { NSLog(@"Yikes 2: failed to set keepalive! ERRNO: %s", strerror(errno)); } } } 

两个选项都打印出SOCK HANDLE:9,没有错误信息。 当WiFi模块closures时,当我不向输出stream发送数据时,连接仍然保持打开30分钟或更长时间。 如果我发送数据到输出stream,我得到NSStreamEventErrorOccurred – 错误域= NSPOSIXErrorDomain代码= 60“操作无法完成。操作超时”约60秒后。 我用苹果设备试了一下。 当我尝试使用iOS模拟器时,我没有看到使用Wireshark的Keepalive数据包。

iOS中的NSStream tcp keepalive也描述了keepalive的设置。 马丁R示例代码激活keepaliveinputstream,似乎是错误的。

是否有可能在苹果iOS设备,如iPhone上激活TCP keepalive(应该按照David H的说法)? 如果有可能应该怎么做(我的代码中缺less什么)?

请参阅: http : //en.wikipedia.org/wiki/Keepalive#TCP_keepalive

通常,keepalive时间( net.inet.tcp.keepidle )默认为7200秒。 我不知道在iOS中是否是这样,但RFC 1122明确要求“ 不less于2小时 ”。

这个间隔必须是可configuration的,并且必须默认不less于两个小时。

那么,你怎么能在你的应用程序中configuration它呢? 尝试:

 #import <sys/types.h> #import <sys/socket.h> #import <netinet/in.h> #import <netinet/tcp.h> int on = 1; int delay = 120; setsockopt(socket_handle, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)); setsockopt(socket_handle, IPPROTO_TCP, TCP_KEEPALIVE, &delay, sizeof(delay)); 

看到man tcp

我证实了这一点在iOS模拟器(iPhone 5s / iOS 8.0)上的作品。

谢谢你rintaro指导我正确的方向。

stream设置保持不变,在我的问题。 我testing了不同的设置,并没有发现我的问题中描述的套接字句柄检测示例之间的差异。

激活与iPod设备和iOS 7.1保持连接的代码

  case NSStreamEventOpenCompleted: @try { if (theStream == outputStream) { NSData *data = (NSData *)[theStream propertyForKey:(__bridge NSString *)kCFStreamPropertySocketNativeHandle]; if(data) { CFSocketNativeHandle socket_handle = *(CFSocketNativeHandle *)[data bytes]; //NSLog(@"SOCK HANDLE: %x", socket_handle); //SO_KEEPALIVE option to activate int option = 1; //TCP_NODELAY option to activate int option2 = 1; //Idle time used when SO_KEEPALIVE is enabled. Sets how long connection must be idle before keepalive is sent int keepaliveIdle = 10; //Interval between keepalives when there is no reply. Not same as idle time int keepaliveIntvl = 2; //Number of keepalives before close (including first keepalive packet) int keepaliveCount = 4; //Time after which tcp packet retransmissions will be stopped and the connection will be dropped.Stream is closed int retransmissionTimeout = 5; if (setsockopt(socket_handle, SOL_SOCKET, SO_KEEPALIVE, &option, sizeof (int)) == -1) { NSLog(@"setsockopt SO_KEEPALIVE failed: %s", strerror(errno)); }else { NSLog(@"setsockopt SO_KEEPALIVE ok"); } if (setsockopt(socket_handle, IPPROTO_TCP, TCP_KEEPCNT, &keepaliveCount, sizeof(int)) == -1) { NSLog(@"setsockopt TCP_KEEPCNT failed: %s", strerror(errno)); }else { NSLog(@"setsockopt TCP_KEEPCNT ok"); } if (setsockopt(socket_handle, IPPROTO_TCP, TCP_KEEPALIVE, &keepaliveIdle, sizeof(int)) == -1) { NSLog(@"setsockopt TCP_KEEPALIVE failed: %s", strerror(errno)); }else { NSLog(@"setsockopt TCP_KEEPALIVE ok"); } if (setsockopt(socket_handle, IPPROTO_TCP, TCP_KEEPINTVL, &keepaliveIntvl, sizeof(int)) == -1) { NSLog(@"setsockopt TCP_KEEPINTVL failed: %s", strerror(errno)); }else { NSLog(@"setsockopt TCP_KEEPINTVL ok"); } if (setsockopt(socket_handle, IPPROTO_TCP, TCP_RXT_CONNDROPTIME, &retransmissionTimeout, sizeof(int)) == -1) { NSLog(@"setsockopt TCP_RXT_CONNDROPTIME failed: %s", strerror(errno)); }else { NSLog(@"setsockopt TCP_RXT_CONNDROPTIME ok"); } if (setsockopt(socket_handle, IPPROTO_TCP, TCP_NODELAY, &option2, sizeof(int)) == -1) { NSLog(@"setsockopt TCP_NODELAY failed: %s", strerror(errno)); }else { NSLog(@"setsockopt TCP_NODELAY ok"); } } } 

当TCP连接空闲时,应用程序会在10秒钟之后开始发送保持连接。 当没有应答时,app开始以2秒的间隔发送keepalive报文,当有4个keepalive报文没有应答时closuresstream。 这意味着,当成功进行Keepalive交换后,如果WiFi模块closures,则在连接丢失时,闲置时最多需要18秒才能closuresstream。

另一个参数是重发超时值。 默认似乎是6秒左右。 这个定时器在有第一个数据包重传时启动。 应用程序尝试重新传输数据包,但是如果数据包重新传输在5秒内失败,stream将closures。

NB! 设备和模拟器的不同结果。

iPod设置激活日志

  • setsockopt SO_KEEPALIVE ok
  • setsockopt TCP_KEEPCNT ok
  • setsockopt TCP_KEEPALIVE ok
  • setsockopt TCP_KEEPINTVL ok
  • setsockopt TCP_RXT_CONNDROPTIME ok
  • setsockopt TCP_NODELAY确定

模拟器设置激活日志

  • setsockopt SO_KEEPALIVE ok
  • setsockopt TCP_KEEPCNT失败:协议不可用
  • setsockopt TCP_KEEPALIVE ok
  • setsockopt TCP_KEEPINTVL失败:协议不可用
  • setsockopt TCP_RXT_CONNDROPTIME ok
  • setsockopt TCP_NODELAY好的

这意味着无法使用iOS模拟器进行激活和跟踪。 我没有find为什么在模拟器中显示错误消息“协议不可用”。