I have a program that is sending large files over NSStreams after some initial processing. The flow of the application goes like this:
1) Two devices connect to each other, open their input and output streams, and schedule their run loops:
[self.inputStream setDelegate:self];
[self.outputStream setDelegate:self];
[self.inputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
[self.outputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
[self.inputStream open];
[self.outputStream open];
2) The Server device sends a message to client indicating everything is good to go and we're all ready. The Client receives this message just fine.
3) The client then selects a list of files that it wants to send to the server and hits "send." The client then spins off a background thread to grab a bunch of files and zip them all up. This can take upwards of a minute or so.
- (void) sendFilesAtPaths: (NSArray *) paths
{
self.paths = [paths copy];
__weak FileSharingClient *weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for(Item *item in weakSelf.paths)
{
//Zip files up, this can take some time but we're on a background thread so it won't lock up the UI
[weakSelf.connection zipFilesAssociatedWithItem:item];
}
//Now that we've done all the heavy work, bounce back to the main thread to actually start sending the data
dispatch_async(dispatch_get_main_queue(), ^{
//Start by sending only the first file, the server will let us know when they are ready for the next one
[weakSelf.connection sendFileWithItem:weakSelf.paths.firstObject];
});
});
}
4) The call to 'sendFileWithItem:' delivers the first burst of information by making the following call:
if([_outputStream hasSpaceAvailable])
{
NSInteger written = [self.outputStream write:buffer maxLength:self.outputHeaderSize];
}
All following data is transferred by sending data out through calls made to - (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)streamEvent
The Problem
The problem I'm seeing here is that if the background thread to zip up these files takes a longer amount of time, the initial call to write data DOES indicate that bytes were written to the stream, but I never receieve any calls into - (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)streamEvent
. The server never receives information. Somewhere around 30 seconds later the write command (I assume?) times out, the client receives an empty string (NOT sent explicitly by me from the server), and the client's connection closes.
If I send over a file that doesn't take long to zip up, I don't see this problem.
Running everything on the main thread - including the zipping process - does not resolve the problem (even if ignoring the fact that it locks up the UI). This would appear to rule out the threading being an issue, but it's all I have to go on right now.
I'm completely out of ideas, I hope someone can help me.
Note: In anticipation of this suggestion, I cannot use the CocoaAsyncSockets library.
In case anyone ever stumbles up on this and just happens to see the same thing I saw (and think the same thing is the problem):
The problem wasn't that the threading was causing issues - just writing that post made me realize how absurd I was being. The problem appears to have been that the underlying TCP sockets were timing out after waiting for so long to receive data. I solved the problem by sending out a heartbeat message once a second, which keeps the sockets alive until I'm ready to actually start sending data.