GKSession 对等点断开连接导致其他对等点显示为断开连接
我的应用程序使用 GKSession 和 GKSessionModePeer。它必须处理对等点的任意连接和断开连接,因为这是一个长时间运行的应用程序,用户应该能够进入后台并稍后返回。这在大多数情况下都工作得很好。但有时,当对等方断开连接时,其他设备会收到 didChangeState:GKPeerStateDisconnected 通知,不仅适用于真正断开连接的设备,还适用于实际仍连接的其他设备。
我可以使用下面的代码和 4 台设备(全部运行 iOS 5)重现此行为。当一切按预期进行时,当设备 A 退出应用程序时,所有其他设备都会收到通知,并且这些设备上的日志输出为:
Service: didChangeState: peer A已断开连接 (12345)
但是过了一会儿,当设备断开连接(再次说 A),其他设备将为未断开连接的设备获得额外的回调。例如,设备 C 将获取:
Service: didChangeState: peer A已断开连接 (...) // 预期
Service: didChangeState: Peer B已断开连接 (...) // 从不断开连接< /code>
大约在同一时间,我有时会在断开连接的设备的日志中看到此类消息,不清楚它们是否确实相关:
dnssd_clientstub DNSServiceRefDeallocate Called with NULL DNSServiceRef
和/或
dnssd_clientstub DNSServiceProcessResult 使用 DNSServiceRef 调用,没有 ProcessReply 函数
一旦发生这种情况,GKSession 似乎处于不良状态,并且不再正确处理连接和断开连接。为了恢复到良好的状态,我必须在所有设备上硬终止该应用程序,稍等一下,然后重新开始。
我尝试过在进入后台时处理 GKSession 的不同方法(仅设置 available=NO 并且不断开连接,根本不执行任何操作),但没有一种方法效果更好。
有其他人遇到过这种行为(并解决了它)吗?
AppDelegate 中的简单重现案例(使用 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
My app uses GKSession with GKSessionModePeer. It has to handle peers arbitrarily connecting and disconnecting because this is a long running app and users should be able to go to background and come back later. This works fine most of the time. But sometimes, when a peer disconnects, other devices get notified with didChangeState:GKPeerStateDisconnected not only for the device that really disconnected but also for other devices that are actually still connected.
I can reproduce this behavior with the code below and 4 devices (all on iOS 5). When everything goes as expected, when device A quits the app, all other devices get notified and the log output on those devices is:
Service: didChangeState: peer A disconnected (12345)
But after a while, when a device disconnects (say A again), other devices will get additional callbacks for devices that didn't disconnect. For example, device C would get:
Service: didChangeState: peer A disconnected (...) // expected
Service: didChangeState: peer B disconnected (...) // never disconnected
Around the same time I am sometimes seeing these kind of messages in the logs of the disconnecting device, not clear if they are actually related:
dnssd_clientstub DNSServiceRefDeallocate called with NULL DNSServiceRef
and/or
dnssd_clientstub DNSServiceProcessResult called with DNSServiceRef with no ProcessReply function
Once this happens, GKSession seems to be in a bad state and no longer correctly handles connects and disconnects. To get back into a good state I have to hard kill the app on all devices, wait a little, and start over.
I have tried different ways of handling GKSession when going to background (only setting available=NO and not disconnecting, not doing anything at all), none of which worked any better.
Has anybody else run into this behavior (and solved it)?
Simple repro case in an AppDelegate (using 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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我从苹果支持人员那里听说,这种断开连接行为的发生是因为设备“通过”彼此连接。例如,设备A通过设备B连接到设备C。如果设备B掉线,设备A会看到设备C断开并立即重新连接。我还没有听说这个问题是否/何时会得到解决。
I heard from apple support that this disconnect behavior is happening because devices connect "through" each other. For example, device A connects to device C through device B. If device B drops, device A will see device C disconnect and reconnect right away. I have not heard if/when this will be fixed.
这可能为时已晚,但我认为如果您将会话模式从
将 GKSessionModePeer 更改为服务器上的 GKSessionModeServer 即可解决该问题。
基本上,对于对等点,它们都相互连接,服务器在技术上以相同的方式工作,但是您会收到有关断开连接的正确通知。
This is probably too late, but I think that if you change the session mode from
GKSessionModePeer to GKSessionModeServer on the server it'll fix the issue.
Basically with peer they all connect to each other, the server one technically works the same way, but you get proper notifications for disconnects.