3

私のアプリはGKSessionModePeerでGKSessionを使用しています。これは長時間実行されるアプリであり、ユーザーはバックグラウンドに戻って後で戻ることができるはずなので、任意に接続および切断するピアを処理する必要があります。これはほとんどの場合正常に機能します。ただし、ピアが切断されると、実際に切断されたデバイスだけでなく、実際に接続されている他のデバイスについても、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を処理するさまざまな方法を試しました(available = NOに設定し、切断せず、何もしません)。どれもうまくいきませんでした。

他の誰かがこの振る舞いに遭遇しましたか(そしてそれを解決しましたか)?

AppDelegateでの単純な再現ケース(アークを使用):

- (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
4

2 に答える 2

1

Apple サポートから、この切断動作は、デバイスが相互に「介して」接続されているために発生していると聞きました。たとえば、デバイス A がデバイス B を介してデバイス C に接続するとします。デバイス B が切断された場合、デバイス A はデバイス C が切断され、すぐに再接続されることを確認します。これが修正されるかどうか、いつ修正されるかは聞いていません。

于 2012-03-06T02:39:16.763 に答える
-1

これはおそらく手遅れですが、サーバーでセッション モードを GKSessionModePeer から GKSessionModeServer に変更すると、問題が解決すると思います。

基本的に、ピアはすべて相互に接続し、サーバーは技術的には同じように動作しますが、切断の適切な通知を受け取ります。

于 2013-03-20T16:36:38.873 に答える