Archived Forum Post

Index of archived forum posts

Question:

Sending large files over socket connection

Feb 06 '13 at 18:07

Hi,

I've a requirement to be sending large files over socket connection in iOS. From what I understand I will have to do the chunking myself.

Here is some sample code that will successfully transfer, in chunks, a huge file. First set up a socket connection and send over the size of the file in string form. Then send chunks over.

CkoFileAccess *fac = [[CkoFileAccess alloc] init];
if (![fac OpenForRead:filePath]) {
    NSLog(@"Open for read error\n%@", fac.FileOpenErrorMsg);
}

NSInteger chunkSize = 1048576;

unsigned long long completedBytes = 0;
unsigned long long fileSize = [[fac FileSize:filePath] longLongValue];

NSString *sizeString = [NSString stringWithFormat:@"%lli-EOM-", fileSize];
success = [socket SendString:sizeString];
if (success != YES) {
    NSLog(@"err 3: %@", socket.LastErrorText);
    return;
}

while (completedBytes < fileSize) {

    NSInteger chunk = (completedBytes + chunkSize < fileSize) ? chunkSize : fileSize - completedBytes;

    NSData *data = [fac FileRead:[NSNumber numberWithInteger:chunk]];

    if (![socket SendBytes:data numBytes:[NSNumber numberWithInteger:chunk]]) {
        NSLog(@"Err - %@", [socket LastErrorText]);
    }

    completedBytes += chunk;
}

On the receiving end I will listen for the size string. Let the length in bytes and keep a byte count as well so I know how many chunks to expect and how many bytes in each chunk.

// get file size
NSString *sizeString = [clientSock ReceiveUntilMatch: @"-EOM-"];
if (receivedMsg == nil ) {
    NSLog(@"err 7: %@", clientSock.LastErrorText);
    return;
}
sizeString = [sizeString stringByReplacingOccurrencesOfString:@"-EOM-" withString:@""];
unsigned long long fileSize = [sizeString longLongValue];

CkoFileAccess *fac = [[CkoFileAccess alloc] init];

if (![fac OpenForWrite:filePath]) {
    NSLog(@"Faield to open for write %@", [fac FileOpenErrorMsg]);
}

unsigned long long receivedBytes = 0;

NSInteger chunkSize = 1048576;

while (receivedBytes < fileSize) {

    NSInteger chunk = (receivedBytes + chunkSize < fileSize) ? chunkSize : fileSize - receivedBytes;

    NSData *data = [clientSock ReceiveBytesN:[NSNumber numberWithInteger:chunk]];

    receivedBytes += chunk;

    if (![fac FileWrite:data]) {
        NSLog(@"failed to write");
    }
}

[fac FileClose];

I can't help but feel I'm doing this in a bit of a roundabout way? Can I make this implementation more efficient? I'm also looking to improve the (non-existent) error handling in the above

thanks!


Answer

Dan,

Your implementation is exactly what I would do if faced with the same task. It's not roundabout at all. In fact, most protocols work this way: IMAP, SMTP, POP3, HTTP, FTP, etc. In different formats, the amount of forthcoming data to be sent is first provided to the server in a separate message so that the server knows how much data is expected.

As far as responding to errors goes -- if a read or write on either side fails, maybe because the other side closes its connection, or stops read/writing prematurely, or because of an external network connectivity problem (i.e. ethernet cable unplugged), then the side that discovers the error should close the connection and report it in any way it sees fit.