Сохранение соединения сокета в iOS

У меня есть следующий код, написанный в Objective-C, который записывает данные в сокет. Сервер работает node.js поверх 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];

Я могу подключиться к серверу и отправить информацию. Однако я заметил, что соединение истекает через несколько минут (я думаю, 5 минут или около того?). Как я могу сохранить это соединение в живых? Я знаю, что есть разъединение, потому что то же самое происходит, если я подключаюсь к серверу под терминалом, я должен сохранить это соединение в живых. Я предполагаю, что то же самое происходит в моем коде. Спасибо за вашу помощь!

Ответ 1

Я нашел, как это сделать. Поскольку сервер работает node.js, я использовал

socket.setKeepAlive(true, 2000);

И таким образом node сохраняет каждый сокет открытым. Значение по умолчанию - false, поэтому именно поэтому время было отключено. Надеюсь, это полезно другим людям. Спасибо за ваши ответы, я думаю, что сохранение сердечного ритма (keep-alive) также является хорошей идеей, но с этим родным подходом node позаботится об этом.

Ответ 2

Самый простой ответ - попытаться использовать опцию сокета SO_KEEPALIVE.

Сломанные соединения могут быть трудно обнаружить без передачи данных между конечными точками, что делает эту опцию в некотором смысле бесполезной, поскольку она не использует данные для обнаружения сломанных соединений. Однако его легко добавить к вашему коду. Добавьте его и посмотрите, поможет ли он...

Вот как это делается на 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
}

Непростой ответ заключается в добавлении пакета ping в ваш протокол и отправке этого пакета ping регулярно, чтобы вы могли обнаружить неисправное соединение.

Ответ 3

Вам необходимо регулярно отправлять мусор через соединение. Попробуйте следующее:

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

Затем объявите это в self:

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

Примечание. Замените "Keepalive!". с тем, что вы хотите для своего протокола. Единственным требованием является то, что удаленный конец (сервер сокетов) игнорирует сообщение.

Ответ 4

Предполагая NSOutputStream * oStream;

В iOS сделайте это так:

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

Я публикую это, так как я просто потратил часы на все это, хочу сохранить кого-то еще.