NSStream TCP Keep-alive iOS

2019-01-14 19:09发布

I have written this code to setup a stream with a server:

-(void)streamOpenWithIp:(NSString *)ip withPortNumber:(int)portNumber;
{
       CFReadStreamRef readStream;
       CFWriteStreamRef writeStream;
       CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (__bridge CFStringRef)ip, portNumber, &readStream, &writeStream);

       if(readStream && writeStream)
       {
            CFReadStreamSetProperty(readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
            CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);

            //Setup inpustream
            inputStream = (__bridge NSInputStream *)readStream;
            [inputStream setDelegate:self];
            [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
            [inputStream open];

            //Setup outputstream
            outputStream = (__bridge NSOutputStream *)writeStream;
            [outputStream setDelegate:self];
            [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
            [outputStream open];
       }
}

I am able to connect to the server and send & receive data. But I want to check if the connection is still there. If I disconnect a cable from the wifi router, I still can send data in the stream and no error occurred.

You can solve this on application level by using a timer to send some message and check if you receive something back. But you can solve this also with TCP Keep-Alive technique on a lower level.

How do I implement this with NSStream? How can I set the checking interval?

I assume that you get a NSStreamEventErrorOcurred when the stream is down by checking it with TCP Keep-Alive?

I have checked this post, but I can't figure it out: Keeping a socket connection alive in iOS

Thanks for your help!

3条回答
太酷不给撩
2楼-- · 2019-01-14 19:44

There is a more complete answer to a similar question.

For the example of the app that starts sending keepalive after 10 seconds, sends keppalive every 2 seconds and closes the stream after 4 keepalives with no reply, take a look at this post: Is it possible to activate TCP keepalive on Apple iOS devices

It also shows how to set retransmission timeout after which the connection is closed.

查看更多
放荡不羁爱自由
3楼-- · 2019-01-14 20:04

You can get the native socket handle with

CFDataRef socketData = CFReadStreamCopyProperty((__bridge CFReadStreamRef)(inputStream), kCFStreamPropertySocketNativeHandle);
CFSocketNativeHandle socket;
CFDataGetBytes(socketData, CFRangeMake(0, sizeof(CFSocketNativeHandle)), (UInt8 *)&socket);
CFRelease(socketData);

and then set socket options (you need to #include <sys/socket.h> for this):

int on = 1;
if (setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) == -1) {
   NSLog(@"setsockopt failed: %s", strerror(errno));
}

You could put this code in the event handler function for the kCFStreamEventOpenCompleted event:

- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)event {
    switch (event) {
        case kCFStreamEventOpenCompleted:
            if (stream == self.inputStream) {
                CFDataRef socketData = CFReadStreamCopyProperty((__bridge CFReadStreamRef)(stream), kCFStreamPropertySocketNativeHandle);
                CFSocketNativeHandle socket;
                CFDataGetBytes(socketData, CFRangeMake(0, sizeof(CFSocketNativeHandle)), (UInt8 *)&socket);
                CFRelease(socketData);

                int on = 1;
                if (setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) == -1) {
                    NSLog(@"setsockopt failed: %s", strerror(errno));
                }
            }
            break;

        /* ... other cases ... */;
    }
}
查看更多
该账号已被封号
4楼-- · 2019-01-14 20:04

this delegate method gives the stream status of connection. you can check your connections status here

 -(void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)event {
    NSLog(@"Stream triggered.");
    switch(event) {
            case (NSStreamStatus)NSStreamEventErrorOccurred:
        {

            NSLog(@"%@",[stream streamError].localizedDescription);

        }
            break;
        case NSStreamEventHasSpaceAvailable: {
            if(stream == outputStream) {
                NSLog(@"outputStream is ready.");
            }
            break;
        }
        case NSStreamEventHasBytesAvailable: {
            if(stream == inputStream) {

                   NSLog(@"data can be read here!");

                }
            }
            break;
        }
        default: {


            NSLog(@"Stream is sending an Event: %i", event);
            break;
        }
    }
}
查看更多
登录 后发表回答