itenyh
itenyh

Reputation: 1939

how to deal with the block of ftp download request

I am downloading file with ftp protocol. Now, to check the ability of dealing with error, I am simulating happening of some network error. The code to handle the network inputStream is as below:

- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
// An NSStream delegate callback that's called when events happen on our 
// network stream.
{
#pragma unused(aStream)
    assert(aStream == self.networkStream);
    switch (eventCode) {
    case NSStreamEventOpenCompleted: {
       self.connected = YES; 
    } break;
    case NSStreamEventHasBytesAvailable: {
        NSInteger       bytesRead;
        uint8_t         buffer[32768];

        // Pull some data off the network.
        bytesRead = [self.networkStream read:buffer maxLength:sizeof(buffer)];
         DLog(@"%@,byteRead:%d",self.urlInput,bytesRead);   
        if (bytesRead == -1) {
            [self _stopReceiveWithStatus:@"Network read error"];
        } else if (bytesRead == 0) {
            [self _stopReceiveWithStatus:@"success"];
        } else {
            NSInteger   bytesWritten;
            NSInteger   bytesWrittenSoFar;

            bytesWrittenSoFar = 0;
            do {
                bytesWritten = [self.fileStream write:&buffer[bytesWrittenSoFar] maxLength:bytesRead - bytesWrittenSoFar];
                DLog(@"%@,bytesWritten:%d",self.urlInput,bytesWritten);
                assert(bytesWritten != 0);
                if (bytesWritten == -1) {
                    [self _stopReceiveWithStatus:@"File write error"];
                    break;
                } else {
                    bytesWrittenSoFar += bytesWritten;
                }
            } while (bytesWrittenSoFar != bytesRead);
        }
    } break;
    case NSStreamEventHasSpaceAvailable: {
        assert(NO);     // should never happen for the output stream
    } break;
    case NSStreamEventErrorOccurred: {
        [self _stopReceiveWithStatus:@"Stream open error"];
    } break;
    case NSStreamEventEndEncountered: {
      assert(NO);
    } break;
    default: {
        assert(NO);
    } break;
}
}

If I turn off the wifi manually or turn off my wireless router (Network connection flag is off), a "NSStreamEventErrorOccurred" will be return and the downloading process will be terminated correctly. However, if I turn off the Modem, while keeping the wireless router open (Network connection flag is on). The downloading process stuck at the case "NSStreamEventHasBytesAvailable". Even after I turn on the internet connection , it is still stuck.

I want to know why it is stuck and how can I detect this kind of error. How can I deal with this situation?

Upvotes: 0

Views: 350

Answers (1)

Conrad Shultz
Conrad Shultz

Reputation: 8808

First, kudos for considering this and running tests. Many developers assume that "the network connection will always work."

Second, it seems a little odd that you are using NSStream for FTP downloads; you do know that NSURLConnection supports FTP, right? Unless you are doing something really strange you should probably use the built-in URL loading facilities.

In any case, the issue here is that there is in principle no way for an application (or computer) to determine whether there has been a pause in a connection because the connection failed (and should be restarted or canceled) or because it simply is running slowly (in which case patience is required).

I'm a little surprised that an active TCP session is not resumed when your modem is reconnected; that suggests that maybe your ISP's router is dropping the connection when the modem link goes down or that your IP is changing on reconnection, but this isn't important for your question anyway.

TCP sessions will ordinarily eventually get timed out by the OS (or an upstream router) after a period of inactivity, but this might be an unacceptably long time. So the thing you probably need to do is implement a timeout.

What I'd probably do (assuming you stay the course with NSStream as discussed above) is have an NSTimer firing off periodically - maybe every 15 seconds - and have the callback compare the current time to a timestamp that you set on each NSStreamEventHasBytesAvailable event. If the timestamp is too old (say, more than 15 seconds), cancel or restart the download as desired and notify the user.

But again, take a look at just using NSURLConnection.

Upvotes: 1

Related Questions