Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
399 views
in Technique[技术] by (71.8m points)

iphone - Is it possible to activate TCP keepalive on Apple iOS devices

Apple device === Router === WiFi module

Apple device(iPhone) is connecting to WiFi module port 2000 with TCP connection. I want to activate TCP keepalive packet sending on Apple device to find out when TCP connection to WiFi module is lost (module is switched off).

My stream setup

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

I tried to activate keepalive according to David H post 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));
                    }
                }



            }

Both options print out SOCK HANDLE: 9 , no error messages. When WiFi module is switched off connection still stays open for 30 minutes or more when I do not send data to outputstream. If I send data to outputstream I get NSStreamEventErrorOccurred - Error Domain=NSPOSIXErrorDomain Code=60 "The operation couldn’t be completed. Operation timed out" after around 60 seconds. I tried this with Apple device. When I tried with iOS Simulator I did not see keepalive packets with Wireshark.

NSStream tcp keepalive in iOS also describes keepalive setup. Martin R example code activates keepalive for inputstream that seems wrong.

Is it possible to activate TCP keepalive on Apple iOS devices like iPhone (should be according to David H)? If it is possible how it should be done (what is missing from my code)?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Thank you rintaro for directing me to right direction.

Stream setup stayed the same as in my question. I tested different setups and did not find difference between socket handle detection examples described in my question.

Code that activates keepalive with iPod device and 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");
                    }
                }
            }

When TCP connection is idle, app starts to send keepalive after 10 second intervals. When there is no answer, app starts to send keepalive packets with 2 second interval and closes stream when there is 4 keepalive packets without reply. This means that when WiFi module is turned off after successful keepalive exchange it takes maximum 18 seconds on idle to close stream when connection is lost.

Another parameter is retransmission timeout value. Default seems to be around 6 seconds. This timer starts when there is first packet retransmission. App tries to retransmit packet but if packet retransmission fails during 5 seconds stream is closed.

NB! Different results in device and simulator.

iPod settings activation log

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

Simulator settings activation log

  • setsockopt SO_KEEPALIVE ok
  • setsockopt TCP_KEEPCNT failed: Protocol not available
  • setsockopt TCP_KEEPALIVE ok
  • setsockopt TCP_KEEPINTVL failed: Protocol not available
  • setsockopt TCP_RXT_CONNDROPTIME ok
  • setsockopt TCP_NODELAY ok

This means that it is not possible to activate and trace with iOS simulator. I did not find why error message "Protocol not available" is displayed in simulator.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

57.0k users

...