2014-09-04 108 views
3

Apple设备===路由器=== WiFi模块是否可以在Apple iOS设备上激活TCP keepalive

Apple设备(iPhone)通过TCP连接连接到WiFi模块端口2000。我想在Apple设备上激活TCP keepalive数据包发送,以了解何时TCP模块的TCP连接丢失(模块关闭)。

我流设置

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]; 

我试图根据大卫小时后Keeping socket connection alive in 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句柄激活存活:9,没有错误消息。当WiFi模块关闭时,当我不向输出流发送数据时,连接仍然保持打开30分钟或更长时间。如果我将数据发送到outputstream,则会在60秒后收到NSStreamEventErrorOccurred - 错误域= NSPOSIXErrorDomain代码= 60“操作无法完成,操作超时”。我尝试过使用Apple设备。当我尝试iOS模拟器时,我没有看到Wireshark的Keepalive数据包。

NSStream tcp keepalive in iOS也描述了keepalive设置。 Martin R示例代码激活了输入流的keepalive,这似乎是错误的。

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

回答

6

看到:http://en.wikipedia.org/wiki/Keepalive#TCP_keepalive

通常情况下,存活时间(net.inet.tcp.keepidle)默认7200sec。 我不知道在iOS中是否属实,但“不少于2小时”显然是RFC 1122所要求的。

此间隔必须是 可配置,并务必默认不少于两个小时。

那么,你怎么配置它在你的应用程序?请尝试:

#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)。

10

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

流设置保持不变,在我的问题。 我测试了不同的设置,并没有发现我的问题中描述的套接字句柄检测示例之间的差异。该激活与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数据包没有应答时关闭数据流。这意味着,当成功进行Keepalive交换后关闭WiFi模块时,闲置时最多需要18秒,以便在连接丢失时关闭流。

另一个参数是重发超时值。默认似乎是6秒左右。该定时器在有第一个数据包重传时启动。 App尝试重新传输数据包,但如果数据包重新传输在5秒内失败,则会关闭数据流。

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

iPod设置激活日志

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

仿真器设置激活日志

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

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

+0

感谢您的详细调查! – rintaro 2014-09-08 14:06:38

+0

如果上述代码中的某些定义未在您的项目中声明,则必须包含以下内容:#include #include # #include Despotovic 2015-12-15 11:35:13

+0

本示例中所选值如何映射到推荐值?我已经尝试过这些设置,但网络在少数设备(约5台)上有些不稳定。我们怀疑这主要是由于'retransmissionTimeout',你会同意吗? – 2015-12-15 18:23:05

相关问题