Сохранение подключения к сокету в 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 минут или около того?). Как сохранить эту связь? Я знаю, что есть разъединение, потому что то же самое происходит, если я подключаюсь к серверу через Терминал, я должен поддерживать это соединение. Я предполагаю, что то же самое происходит в моем коде. Спасибо за вашу помощь!


person Hahnemann    schedule 08.07.2012    source источник
comment
Работающий код Objective-C см. в следующем ответе...   -  person Despotovic    schedule 04.02.2016


Ответы (4)


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

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

Вот как это делается в C или 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, чтобы вы могли обнаружить разрыв соединения.

person JimR    schedule 08.07.2012
comment
Хм. Есть ли способ сделать это, используя API выше в Objective-C? - person Hahnemann; 09.07.2012
comment
@Hahnemann: я плохо знаю Objective-C, поэтому не знаю. Тем не менее, я готов поспорить, что есть аварийный люк для вызовов C-библиотек. - person JimR; 09.07.2012
comment
@Hahnemann: взгляните сюда: developer.apple.com/library/ios/DOCUMENTATION/CoreFoundation/ и посмотрите, поможет ли это. - person JimR; 09.07.2012
comment
@JimR: я получаю это сообщение об ошибке: не удалось установить поддержку активности! ERRNO: операция сокета без сокета. В чем может быть причина и возможное решение? - person Jayprakash Dubey; 13.10.2014
comment
Привет, что такое getNativeSocketHandle()? - person Hardik Mamtora; 18.03.2015

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

socket.setKeepAlive(true, 2000);

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

person Hahnemann    schedule 11.07.2012
comment
вы начали с потоков ios, а теперь работаете с объектом сокета? какой апи ты здесь используешь? - person Itay Levin; 26.12.2012
comment
@ItayLevin он показывает вам, как он сделал это на сервере - см. ниже мой ответ о том, как это сделать на iOS. - person David H; 11.02.2013

Предполагая, что 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));
    }

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

person David H    schedule 10.02.2013
comment
каковы значения socket_handle, SOL_SOCKET, SO_KEEPALIVE? - person Rohan; 13.05.2013
comment
man 2 setsockopt или выполните поиск по setsockopt в представлении документации Xcode. - person David H; 13.05.2013
comment
Только Apple могла сделать что-то настолько простое и такое сложное. Я программировал сетевые приложения, используя сокеты Berkley, и это не так сложно, как это. - person deusprogrammer; 13.01.2015
comment
Где нужно написать этот метод? - person Hardik Mamtora; 18.03.2015
comment
@HardikMamtora сразу после того, как вы получите ссылку на выходной поток. - person David H; 18.03.2015

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

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!" с тем, что вы хотите для вашего протокола. Единственное требование состоит в том, чтобы удаленный конец (сервер сокетов) игнорировал сообщение.

person Linuxios    schedule 09.07.2012
comment
есть ли установленный лимит времени? Мне интересно, с какой частотой я должен отправлять мусорные данные. - person Just a coder; 09.07.2012
comment
@user: Сколько времени нужно, чтобы соединение прервалось? Запланируйте его немного раньше. - person Linuxios; 09.07.2012
comment
в некоторых случаях вместо мусора вы можете рассматривать это как данные пульса на сервере сокетов. ПРОГОЛОСОВАНО! :) - person tony gil; 17.05.2013
comment
@Linuxios: что такое sockfd? - person Jayprakash Dubey; 13.10.2014
comment
@tonygil: я использую URL, а не IP-адрес. Будет ли проблема? Кроме того, как мне это решить? - person Jayprakash Dubey; 13.10.2014
comment
@JayprakashDubey понятия не имею, извините. после того, как вы попробуете это, вы можете сообщить нам? спасибо - person tony gil; 14.10.2014