Reputation: 77
Well at least unexpected for me... This is the situation: I open a Socket using the following code:
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (CFStringRef)ipaddress , 3333,&readStream, &writeStream);
if(!CFReadStreamOpen(readStream) || !CFWriteStreamOpen(writeStream))
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Connection error"
message:@"There has been a connection error, please ensure your internet connection is working"
delegate:self
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alert show];
return;
}
This all goes fine, next thing I do is setup a callback:
CFStreamClientContext ctxt = {0,(void*)NULL,NULL,NULL,NULL};
static const CFOptionFlags kReadNetworkEvents = kCFStreamEventEndEncountered |
kCFStreamEventErrorOccurred |
kCFStreamEventHasBytesAvailable |
kCFStreamEventOpenCompleted |
kCFStreamEventNone;
CFReadStreamSetClient(readStream, kReadNetworkEvents, ReadStreamClientCallBack, &ctxt);
CFReadStreamScheduleWithRunLoop(readStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
Also works fine, I'll list the callback method to be complete:
static void ReadStreamClientCallBack( CFReadStreamRef stream, CFStreamEventType type, void *clientCallBackInfo ) {
switch (type)
{
case kCFStreamEventEndEncountered:
{
break;
}
case kCFStreamEventErrorOccurred:
break;
case kCFStreamEventHasBytesAvailable:
{
[this stopListen];
UInt8 buffer[1024];
int count = CFReadStreamRead(stream, buffer, 1024);
CFStreamStatus status = CFReadStreamGetStatus(this.readStream);
CFErrorRef error = CFReadStreamCopyError (this.readStream);
CFStringRef errorCode = CFErrorCopyDescription(error);
// Bunch of other irrelevant code
Now what goes wrong: All this code works perfectly fine as long as I'm staying in the application, even if I exit the application and enter it again it works still fine. If I enter standby while the application is open it also works fine. However if I exit the application, put my phone on standby, get my phone out of standby and reenter the application, the call back immediatly gets called, with eventtype kCFStreamEventHasBytesAvailable, even though I'm 100% sure no bytes have been send. If I then call CFReadStreamRead it returns -1 to me, since this means an error occured I figured out the error code which is 57, this mean that the socket has been closed.
Am I overlooking a certain aspect of socket programming on the iPhone? I must admit I'm new to it. Is it not possible to keep a TCP Socket open while out of the application (and entering standby)? I have tried to call CFReadStreamOpen again which returned false to me.
I'm lost here, please help!
Thanks in advance
Upvotes: 0
Views: 2342
Reputation: 22787
Part of the multi-tasking features in 4.0+ require extra coding to support keeping connections alive and performing tasks in the background. You are probably just a victim of the OS taking back those resources since you didn't "opt-in" for them at the appropriate time.
This section covers the basics of backgrounding:
Specifically this:
Be prepared to handle connection failures in your network-based sockets. The system may tear down socket connections while your application is suspended for any number of reasons. As long as your socket-based code is prepared for other types of network failures, such as a lost signal or network transition, this should not lead to any unusual problems. When your application resumes, if it encounters a failure upon using a socket, simply reestablish the connection.
However, there are three very special things that you can do in the background, and if you do you should declare them in your Info.plist
to be a good App citizen. These three values are in the UIBackgroundModes
property and they are audio
location
and voip
You can request more time for your task by registering a block (even though it isn't guarunteed you'll get it) like this:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
UIApplication* app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
// Start the long-running task and return immediately.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Do the work associated with the task.
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
});
}
See also A Short Practical Guide to Blocks for a quick overview and the Concurrency Programming Guide for more detailed information.
Upvotes: 1