我的应用将GKSession与GKSessionModePeer一起使用。它必须处理任意连接和断开连接的对等端,因为这是一个长期运行的应用程序,用户应该能够转到后台并稍后返回。这在大多数情况下都可以正常工作。但有时,当对等设备断开连接时,其他设备会收到didChangeState的通知:GKPeerStateDisconnected不仅用于真正断开连接的设备,还用于实际仍然连接的其他设备。
我可以使用下面的代码和4个设备(所有在iOS 5上)重现此行为。当一切按预期进行时,当设备A退出应用程序时,所有其他设备都会收到通知,并且这些设备上的日志输出为:
Service: didChangeState: peer A disconnected (12345)
但过了一段时间,当设备断开连接(再次说A)时,其他设备将获得未断开连接的设备的额外回调。例如,设备C将获得:
Service: didChangeState: peer A disconnected (...) // expected
Service: didChangeState: peer B disconnected (...) // never disconnected
大约在同一时间我有时会在断开设备的日志中看到这些消息,不清楚它们是否真的相关:
dnssd_clientstub DNSServiceRefDeallocate called with NULL DNSServiceRef
和/或
dnssd_clientstub DNSServiceProcessResult called with DNSServiceRef with no ProcessReply function
一旦发生这种情况,GKSession似乎处于不良状态,不再正确处理连接和断开连接。要恢复到良好的状态,我必须在所有设备上硬杀死应用程序,等待一点,然后重新开始。
我在尝试后台时尝试了不同的处理GKSession的方法(只设置可用= NO而不是断开连接,根本没有做任何事情),没有一种能更好地工作。
有没有其他人遇到这种行为(并解决了)?
AppDelegate中的简单repro案例(使用arc):
- (void)startGKSession
{
self.gkSession = [[GKSession alloc] initWithSessionID:nil displayName:nil sessionMode:GKSessionModePeer];
gkSession.disconnectTimeout = 10;
gkSession.delegate = self;
gkSession.available = YES;
}
- (void)shutdownGKSession
{
gkSession.available = NO;
[gkSession disconnectFromAllPeers];
gkSession.delegate = nil;
gkSession = nil;
[self.connectedDevices removeAllObjects];
}
- (void)connectToPeer:(NSString *)peerId
{
[gkSession connectToPeer:peerId withTimeout:10];
}
- (void)session:(GKSession *)session peer:(NSString *)peerId didChangeState:(GKPeerConnectionState)state
{
switch (state) {
case GKPeerStateAvailable:
NSLog(@"Service: didChangeState: peer %@ available, connecting (%@)", [session displayNameForPeer:peerId], peerId);
[self performSelector:@selector(connectToPeer:) withObject:peerId afterDelay:.5];
break;
case GKPeerStateUnavailable:
NSLog(@"Service: didChangeState: peer %@ unavailable (%@)", [session displayNameForPeer:peerId], peerId);
break;
case GKPeerStateConnected:
NSLog(@"Service: didChangeState: peer %@ connected (%@)", [session displayNameForPeer:peerId], peerId);
break;
case GKPeerStateDisconnected:
NSLog(@"Service: didChangeState: peer %@ disconnected (%@)", [session displayNameForPeer:peerId], peerId);
break;
case GKPeerStateConnecting:
NSLog(@"Service: didChangeState: peer %@ connecting (%@)", [session displayNameForPeer:peerId], peerId);
break;
}
}
- (void)session:(GKSession *)session didReceiveConnectionRequestFromPeer:(NSString *)peerID
{
[session acceptConnectionFromPeer:peerID error:nil];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.connectedDevices = [[NSMutableArray alloc] init];
[self startGKSession];
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.viewController = [[ViewController alloc] initWithNibName:@"ViewController" bundle:nil];
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[self shutdownGKSession];
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
[self startGKSession];
}
@end
答案 0 :(得分:1)
我从苹果支持部门获悉,这种断开行为正在发生,因为设备相互“连通”。例如,设备A通过设备B连接到设备C.如果设备B掉落,设备A将看到设备C断开并立即重新连接。我没有听说是否/什么时候修复。
答案 1 :(得分:-1)
这可能为时已晚,但我认为如果您更改会话模式 GKSessionModePeer到服务器上的GKSessionModeServer它将解决问题。
基本上,对等设备彼此连接,服务器技术上的工作方式相同,但是您可以获得正确的断开连接通知。