iOS NSStream FTP не отвечает после 5 минут бездействия

Я использую библиотеку FTP для iOS (nkreipke/FTPManager). Это прекрасно работает. Я могу загрузить список каталогов, загрузить изображения и т. д. Проблема в том, что если вы оставите приложение открытым примерно на 5 минут, ничего не делая, а затем попытаетесь загрузить или загрузить, ничего не произойдет.

Я отлаживал его и обнаружил, что метод NSStreamDelegate - (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent никогда не вызывается после этого короткого периода времени бездействия.

Вот код:

- (NSArray*) _contentsOfServer:(FMServer*)server {
BOOL success = YES;

action = _FMCurrentActionContentsOfServer;

fileSize = 0;

self.directoryListingData = [[NSMutableData alloc] init];

NSURL* dest = [server.destination ftpURLForPort:server.port];
And(success, (dest != nil));
Check(success);

if (![dest.absoluteString hasSuffix:@"/"]) {
    //if the url does not end with an '/' the method fails.
    //no problem, we can fix this.
    dest = [NSURL URLWithString:[NSString stringWithFormat:@"%@/",dest.absoluteString]];
}

CFReadStreamRef readStream = CFReadStreamCreateWithFTPURL(NULL, (__bridge CFURLRef)dest);
And(success, (readStream != NULL));
if (!success) return nil;
self.serverReadStream = (__bridge_transfer NSInputStream*) readStream;

And(success, [self.serverReadStream setProperty:server.username forKey:(id)kCFStreamPropertyFTPUserName]);
And(success, [self.serverReadStream setProperty:server.password forKey:(id)kCFStreamPropertyFTPPassword]);
if (!success) return nil;

self.bufferOffset = 0;
self.bufferLimit = 0;

currentRunLoop = CFRunLoopGetCurrent();

self.serverReadStream.delegate = self;
[self.serverReadStream open];
[self.serverReadStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

CFRunLoopRun(); //<- Hangs here.

And(success, streamSuccess);
if (!success) return nil;

NSArray* directoryContents = [self _createListingArrayFromDirectoryListingData:self.directoryListingData];
self.directoryListingData = nil;

return directoryContents;

serverReadStream — это объект NSInputStream.

Когда действие заканчивается:

if (self.serverReadStream) {
    [self.serverReadStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    CFRunLoopStop(CFRunLoopGetCurrent());
    self.serverReadStream.delegate = nil;
    [self.serverReadStream close];
    self.serverReadStream = nil;
}

Этот код находится по адресу FTPManager.m. Я искал ответы в Интернете, но не мог найти ни одного. Я не знаю, почему метод NSStreamDelegate вызывается при постоянном использовании, но после некоторого времени бездействия он не вызывается.

Было бы неплохо, если бы кто-нибудь мог мне помочь.

Спасибо.


person Axort    schedule 04.12.2014    source источник
comment
Как вы это исправили? Спасибо   -  person Pablo Martinez    schedule 08.07.2016


Ответы (1)


Я использую ту же библиотеку в своем приложении для iPad, и я застрял в том же вызове CFRunLoopRun(), когда пытался обновить содержимое FTP-сервера при возобновлении моего приложения.

Мне пришлось внести два изменения в код FTPManager.

Во-первых, я изменил порядок

[self.serverReadStream open];
[self.serverReadStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

to

[self.serverReadStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[self.serverReadStream open];

Причина в том, что поток не может быть открыт. Событие ошибки возникает во время вызова открытия, но если поток не запланирован для цикла выполнения, мы никогда не получим событие ошибки. Таким образом, планирование перед открытием позволит нам получить событие ошибки.

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

Добавление следующей строки в конфигурацию потока решило проблему на моей стороне:

[self.serverReadStream setProperty: (id) kCFBooleanFalse forKey: (id) kCFStreamPropertyFTPAttemptPersistentConnection];

Надеюсь это поможет.

person Vincent    schedule 12.02.2015