Skip to content
This repository was archived by the owner on Jan 11, 2023. It is now read-only.

Commit f459630

Browse files
MbaizeJoe Hainline and Micah Baize
authored andcommitted
Reconnect sockets on iOS when resuming from background
This allows playback to be restarted, and GCDWebServer doesn't have to be reinitialized.
1 parent fbcf3fa commit f459630

1 file changed

Lines changed: 112 additions & 12 deletions

File tree

GCDWebServer/Core/GCDWebServer.m

Lines changed: 112 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,8 @@ @interface GCDWebServer () {
177177
dispatch_source_t _source6;
178178
CFNetServiceRef _registrationService;
179179
CFNetServiceRef _resolutionService;
180+
int ipv4ListeningSocket;
181+
int ipv6ListeningSocket;
180182
DNSServiceRef _dnsService;
181183
CFSocketRef _dnsSocket;
182184
CFRunLoopSourceRef _dnsSource;
@@ -463,6 +465,11 @@ - (int)_createListeningSocket:(BOOL)useIPv6
463465
error:(NSError**)error {
464466
int listeningSocket = socket(useIPv6 ? PF_INET6 : PF_INET, SOCK_STREAM, IPPROTO_TCP);
465467
if (listeningSocket > 0) {
468+
if (!useIPv6) {
469+
ipv4ListeningSocket = listeningSocket;
470+
} else {
471+
ipv6ListeningSocket = listeningSocket;
472+
}
466473
int yes = 1;
467474
setsockopt(listeningSocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
468475

@@ -570,12 +577,7 @@ - (BOOL)_start:(NSError**)error {
570577
}
571578
}
572579

573-
struct sockaddr_in6 addr6;
574-
bzero(&addr6, sizeof(addr6));
575-
addr6.sin6_len = sizeof(addr6);
576-
addr6.sin6_family = AF_INET6;
577-
addr6.sin6_port = htons(port);
578-
addr6.sin6_addr = bindToLocalhost ? in6addr_loopback : in6addr_any;
580+
struct sockaddr_in6 addr6 = [self generateAddressWithPort:port bindToLocalhost:bindToLocalhost];
579581
int listeningSocket6 = [self _createListeningSocket:YES localAddress:&addr6 length:sizeof(addr6) maxPendingConnections:maxPendingConnections error:error];
580582
if (listeningSocket6 <= 0) {
581583
close(listeningSocket4);
@@ -740,6 +742,81 @@ - (void)_stop {
740742

741743
#if TARGET_OS_IPHONE
742744

745+
- (void)resetIpv4SocketIfError {
746+
int error = 0;
747+
socklen_t len = sizeof(error);
748+
int retval = getsockopt(ipv4ListeningSocket, SOL_SOCKET, SO_ERROR, &error, &len);
749+
750+
if (retval != 0) {
751+
/* there was a problem getting the error code */
752+
GWS_LOG_ERROR(@"error getting socket error code: %s\n", strerror(retval));
753+
return;
754+
}
755+
756+
if (error != 0) {
757+
/* socket has a non zero error status */
758+
GWS_LOG_INFO(@"Socket error: %s on socket %d\n", strerror(error), ipv4ListeningSocket);
759+
dispatch_source_cancel(_source4);
760+
_source4 = nil;
761+
NSUInteger port = [_GetOption(_options, GCDWebServerOption_Port, @0) unsignedIntegerValue];
762+
BOOL bindToLocalhost = [_GetOption(_options, GCDWebServerOption_BindToLocalhost, @NO) boolValue];
763+
NSUInteger maxPendingConnections = [_GetOption(_options, GCDWebServerOption_MaxPendingConnections, @16) unsignedIntegerValue];
764+
765+
struct sockaddr_in addr4 = [self generateIpv4AddressWithPort:port bindToLocalhost:bindToLocalhost];
766+
NSError* nsError = nil;
767+
int listeningSocket4 = [self _createListeningSocket:NO
768+
localAddress:&addr4
769+
length:sizeof(addr4)
770+
maxPendingConnections:maxPendingConnections
771+
error:&nsError];
772+
773+
if (listeningSocket4 <= 0) {
774+
GWS_LOG_ERROR(@"Failed to create the IPv4 socket\n");
775+
}
776+
_source4 = [self _createDispatchSourceWithListeningSocket:listeningSocket4 isIPv6:NO];
777+
// Need to investigate
778+
dispatch_resume(_source4);
779+
}
780+
}
781+
782+
- (void)resetIpv6SocketIfError {
783+
int error = 0;
784+
NSError* nsError = nil;
785+
socklen_t len = sizeof(error);
786+
int retval = getsockopt(ipv6ListeningSocket, SOL_SOCKET, SO_ERROR, &error, &len);
787+
788+
if (retval != 0) {
789+
/* there was a problem getting the error code */
790+
GWS_LOG_ERROR(@"error getting socket error code: %s\n", strerror(retval));
791+
return;
792+
}
793+
794+
if (error != 0) {
795+
/* socket has a non zero error status */
796+
GWS_LOG_INFO(@"Socket error: %s on socket %d\n", strerror(error), ipv6ListeningSocket);
797+
798+
dispatch_source_cancel(_source6);
799+
_source6 = nil;
800+
NSUInteger port = [_GetOption(_options, GCDWebServerOption_Port, @0) unsignedIntegerValue];
801+
BOOL bindToLocalhost = [_GetOption(_options, GCDWebServerOption_BindToLocalhost, @NO) boolValue];
802+
NSUInteger maxPendingConnections = [_GetOption(_options, GCDWebServerOption_MaxPendingConnections, @16) unsignedIntegerValue];
803+
804+
struct sockaddr_in6 addr6 = [self generateAddressWithPort:port bindToLocalhost:bindToLocalhost];
805+
int listeningSocket6 = [self _createListeningSocket:YES
806+
localAddress:&addr6
807+
length:sizeof(addr6)
808+
maxPendingConnections:maxPendingConnections
809+
error:&nsError];
810+
if (listeningSocket6 <= 0) {
811+
GWS_LOG_ERROR(@"Failed to create the IPv6 socket\n");
812+
}
813+
_source6 = [self _createDispatchSourceWithListeningSocket:listeningSocket6 isIPv6:YES];
814+
815+
// Need to investigate
816+
dispatch_resume(_source6);
817+
}
818+
}
819+
743820
- (void)_didEnterBackground:(NSNotification*)notification {
744821
GWS_DCHECK([NSThread isMainThread]);
745822
GWS_LOG_DEBUG(@"Did enter background");
@@ -751,13 +828,39 @@ - (void)_didEnterBackground:(NSNotification*)notification {
751828
- (void)_willEnterForeground:(NSNotification*)notification {
752829
GWS_DCHECK([NSThread isMainThread]);
753830
GWS_LOG_DEBUG(@"Will enter foreground");
754-
if (!_source4) {
831+
832+
if (_suspendInBackground && !_source4) {
755833
[self _start:NULL]; // TODO: There's probably nothing we can do on failure
756834
}
835+
836+
if ([self isRunning]) {
837+
[self resetIpv4SocketIfError];
838+
[self resetIpv6SocketIfError];
839+
}
757840
}
758841

759842
#endif
760843

844+
- (struct sockaddr_in)generateIpv4AddressWithPort:(NSInteger)port bindToLocalhost:(BOOL)bindToLocalhost {
845+
struct sockaddr_in addr4;
846+
bzero(&addr4, sizeof(addr4));
847+
addr4.sin_len = sizeof(addr4);
848+
addr4.sin_family = AF_INET;
849+
addr4.sin_port = htons(port);
850+
addr4.sin_addr.s_addr = bindToLocalhost ? htonl(INADDR_LOOPBACK) : htonl(INADDR_ANY);
851+
return addr4;
852+
}
853+
854+
- (struct sockaddr_in6)generateAddressWithPort:(NSInteger)port bindToLocalhost:(BOOL)bindToLocalhost {
855+
struct sockaddr_in6 addr6;
856+
bzero(&addr6, sizeof(addr6));
857+
addr6.sin6_len = sizeof(addr6);
858+
addr6.sin6_family = AF_INET6;
859+
addr6.sin6_port = htons(port);
860+
addr6.sin6_addr = bindToLocalhost ? in6addr_loopback : in6addr_any;
861+
return addr6;
862+
}
863+
761864
- (BOOL)startWithOptions:(NSDictionary*)options error:(NSError**)error {
762865
if (_options == nil) {
763866
_options = options ? [options copy] : @{};
@@ -774,8 +877,8 @@ - (BOOL)startWithOptions:(NSDictionary*)options error:(NSError**)error {
774877
#if TARGET_OS_IPHONE
775878
if (_suspendInBackground) {
776879
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_didEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
777-
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_willEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil];
778880
}
881+
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_willEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil];
779882
#endif
780883
return YES;
781884
} else {
@@ -791,10 +894,7 @@ - (BOOL)isRunning {
791894
- (void)stop {
792895
if (_options) {
793896
#if TARGET_OS_IPHONE
794-
if (_suspendInBackground) {
795-
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil];
796-
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillEnterForegroundNotification object:nil];
797-
}
897+
[[NSNotificationCenter defaultCenter] removeObserver:self];
798898
#endif
799899
if (_source4) {
800900
[self _stop];

0 commit comments

Comments
 (0)