Keeping a socket connection alive in iOS
Asked Answered
V

4

11

I have the following code written in Objective-C that writes data to a socket. The server is running node.js on top of Ubuntu:

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

I am able to connect to the server and send information. However I noticed the connection times out after a few minutes (I think 5 minutes or so?). How can I keep this connection alive? I know there is a disconnect because the same thing happens if I connect to the server under Terminal, I have to keep that connection alive. I imagine the same thing is happening in my code. Thanks for your help!

Various answered 8/7, 2012 at 23:29 Comment(1)
For Objective-C code that works, please refer to the following answer...Perilous
V
3

I found how to do this. Since the server is running node.js, I used

socket.setKeepAlive(true, 2000);

And in this way node keeps each socket open. The default is false, so this is why it was timing out. I hope this is useful to other people. Thank your for your responses, I think keeping a heartbeat (keep-alive) is also a good idea, but with this native approach node takes care of it.

Various answered 11/7, 2012 at 4:44 Comment(2)
you started with ios streams and now you are working with socket object? what kind of api are you using here?Marked
@ItayLevin he's showing you how he did it on the server - see below answer by me for how to do it on iOS.Vogele
M
6

The simplest answer is to try to use the SO_KEEPALIVE socket option.

Broken connections can be hard to detect without data flowing between the end-points which is what makes this option in some ways useless as it doesn't use data to detect broken connections. However, it is easy to add to your code to see. Add it and see if it helps...

This is how it's done in C or 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
}

The not-so-simple answer is to add a ping packet to your protocol and send that ping packet regularly so you can detect a broken connection.

Medlin answered 8/7, 2012 at 23:58 Comment(5)
Hmm. Is there a way to do this using the API above in Objective-C?Various
@Hahnemann: I do not know Objective-C well, so I do not know. However, I'm willing to bet there's an escape hatch to make calls to C libs.Medlin
@Hahnemann: Take a look here: developer.apple.com/library/ios/DOCUMENTATION/CoreFoundation/… and see if it helps.Medlin
@Medlin : I'm getting this error message : "failed to set keepalive! ERRNO: Socket operation on non-socket" What might be reason and possible solution?Standup
Hi, what is getNativeSocketHandle()Vinaigrette
E
3

You need to routinely send some garbage over the connection. Try this:

NSTimer* t = [NSTimer scheduledTimerWithTimeInterval:240 target:self selector:@selector(timerMethod:) userInfo:[NSNumber numberWithInt:sockfd] repeats:YES];

Then declare this in self:

-(void)timerMethod:(NSTimer*)t
{
  send([[t userInfo] intValue], "Keepalive!", 11, 0);
}

Note: Replace `"Keepalive!" with whatever you want for your protocol. The only requirement is that the remote end (socket server) ignores the message.

Egad answered 9/7, 2012 at 0:5 Comment(6)
is there a set time out limit? I'm wondering at what frequency i should send garbage data.Tenebrific
@user: How long does it take for the connection to die? Schedule it a little before that.Egad
in some cases, instead of "garbage", you might want to consider this as "heartbeat data" on the socket server. UPVOTED! :)Hyperpituitarism
@Egad : What is sockfd?Standup
@tonygil : I'm using URL not IP address. Will there be an issue? Also, how do I resolve it?Standup
@JayprakashDubey i have NO idea, sorry. after you try it out, can you inform us? tks.Hyperpituitarism
V
3

I found how to do this. Since the server is running node.js, I used

socket.setKeepAlive(true, 2000);

And in this way node keeps each socket open. The default is false, so this is why it was timing out. I hope this is useful to other people. Thank your for your responses, I think keeping a heartbeat (keep-alive) is also a good idea, but with this native approach node takes care of it.

Various answered 11/7, 2012 at 4:44 Comment(2)
you started with ios streams and now you are working with socket object? what kind of api are you using here?Marked
@ItayLevin he's showing you how he did it on the server - see below answer by me for how to do it on iOS.Vogele
V
3

Assuming a NSOutputStream *oStream;

On iOS do it this way:

#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));
    }

I'm posting this since I just spent hours working it all out, want to save someone else the effort.

Vogele answered 10/2, 2013 at 23:32 Comment(5)
what are the values of socket_handle, SOL_SOCKET, SO_KEEPALIVE ?Hostage
man 2 setsockopt or search on setsockopt in the Xcode documentation view.Vogele
Only Apple could make something so simple, so complicated. I have programmed networked apps using Berkley Sockets and it's less complicated than this.Lewislewisite
Where need to write this method ?Vinaigrette
@HardikMamtora just after you get a reference to the output stream.Vogele

© 2022 - 2024 — McMap. All rights reserved.