Is it possible to activate TCP keepalive on Apple iOS devices
苹果设备===路由器=== WiFi模块
Apple设备(iPhone)正在通过TCP连接连接到WiFi模块端口2000。我想激活在Apple设备上发送的TCP keepalive数据包,以查找与WiFi模块的TCP连接丢失(模块关闭)。
我的流设置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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]; |
我试图根据David H的帖子激活keepalive,使iOS中的套接字连接保持活动状态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | - (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模块关闭时,当我不发送数据至outputstream时,连接仍保持打开状态30分钟或更长时间。如果我将数据发送到输出流,则会收到NSStreamEventErrorOccurred-错误域= NSPOSIXErrorDomain代码= 60大约60秒后,"操作无法完成。操作超时"。我在Apple设备上尝试过此操作。当我尝试使用iOS Simulator时,Wireshark没有看到keepalive数据包。
iOS中的NSStream tcp keepalive还介绍了keepalive设置。 Martin R示例代码为看似错误的输入流激活了保持活动状态。
是否可以在iPhone等Apple iOS设备上激活TCP keepalive(应根据David H)?如果有可能应该怎么做(我的代码中缺少什么)?
谢谢林太郎指导我正确的方向。
流设置与我的问题相同。
我测试了不同的设置,但没有发现问题中描述的套接字句柄检测示例之间的差异。
使用iPod设备和iOS 7.1激活keepalive的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | 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秒间隔后开始发送保持活动状态。如果没有答案,则应用会以2秒的间隔开始发送Keepalive数据包,并在有4个Keepalive数据包无应答时关闭流。这意味着,在成功进行Keepalive交换后关闭WiFi模块时,如果连接断开,空闲时最多需要18秒钟才能关闭流。
另一个参数是重传超时值。默认值似乎在6秒左右。当第一次有数据包重传时,此计时器启动。应用会尝试重传数据包,但如果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确定
这意味着无法使用iOS模拟器激活和跟踪。
我没有发现为什么模拟器中显示错误消息" Protocol not available"。
参见:http://en.wikipedia.org/wiki/Keepalive#TCP_keepalive
通常,默认情况下,保持活动时间(
我不知道iOS是否正确,但是RFC 1122明确要求"不少于2小时"。
This interval MUST be
configurable and MUST default to no less than two hours.
那么,如何在应用程序中配置它呢?尝试:
1 2 3 4 5 6 7 8 9 | #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)); |
参见
我确认这可以在iOS Simulator(iPhone 5s / iOS 8.0)上使用。