重新连接断开的对等节点

30

我正在我的应用中使用iOS 7 Multipeer框架,但是我遇到了设备断开的问题。如果我在两个设备上打开应用程序:设备A和设备B,则这两个设备会自动连接到彼此。然而,几秒钟后,设备A会从设备B断开连接。即,首先连接如下:

A ---> B
A <--- B

几秒钟后:

A ---> B
A      B

设备A保持连接,但设备B出现了MCSessionStateNotConnected状态。

这意味着A可以向B发送数据,但B无法回复。我尝试通过检查设备是否已连接并重新初始化连接来规避此问题,方法如下:

[browser invitePeer:peerID toSession:_session withContext:Nil timeout:10];

但是didChangeState回调函数只会使用MCSessionStateNotConnected被调用。

奇怪的是,如果我将应用程序A发送到后台,然后重新打开它,B将重新连接到它并且连接将保持。

Multipeer API(和文档)似乎有点不够详细,所以我一直认为它会正常工作。在这种情况下,我应该如何重新连接设备?


这是本地问题还是物理问题?你尝试过在B到A之间做一些tracepath吗? - user2284570
我非常确定这不是一个物理问题,因为我已经能够使用DNS-SD和CFSockets手动获取稳定的蓝牙连接。这似乎是一个MultiPeer问题。 - James Andrews
啊抱歉,我以为它是通过互联网远程的,但原来是蓝牙! - user2284570
你是否同时浏览和广告?A和B是否都邀请并接受? - ChrisH
是的 - 我只是想确认你和我处在同样的情况下,然后再给出答案。 - ChrisH
8个回答

23

我遇到了同样的问题,似乎与我在应用程序浏览和同时广告有关,并且发送/接受了两个邀请。当我停止这样做并让一个对等方推迟邀请另一个时,设备保持连接。

在我的浏览器委托中,我正在检查已发现对等方的 displayName 的哈希值,并仅在我的对等方具有更高的哈希值时发送邀请:

编辑

正如@ Masa指出的那样, NSString 哈希值在32位和64位设备上会有所不同,因此最好使用 compare: 方法比较 displayName

- (void)browser:(MCNearbyServiceBrowser *)browser foundPeer:(MCPeerID *)peerID withDiscoveryInfo:(NSDictionary *)info {

    NSLog(@"Browser found peer ID %@",peerID.displayName);       

    //displayName is created with [[NSUUID UUID] UUIDString]

    BOOL shouldInvite = ([_myPeerID.displayName compare:peerID.displayName]==NSOrderedDescending);

    if (shouldInvite){
        [browser invitePeer:peerID toSession:_session withContext:nil timeout:1.0]; 
    }
    else {
        NSLog(@"Not inviting");
    }
}

正如你所说,文档很稀少,所以谁知道苹果真正想让我们做什么,但我已经尝试过使用单个会话发送和接受邀请,以及为每个接受/发送的邀请创建一个新会话,但是这种特定的方法给了我最大的成功。


4
非常有帮助,谢谢。请注意,NSString的哈希返回一个NSUInteger。因此,在32位和64位系统上结果是不同的。所以我遇到了没有人发送邀请的情况。我通过对显示名称进行字符串比较来解决了这个问题,而不是使用哈希。(此外,还要注意小心使用NSString的哈希函数:http://www.abakia.de/blog/2012/12/05/nsstring-hash-is-bad/) - Masa
@Masa,这很有趣,谢谢。我一定会改用字符串比较。 - ChrisH
设备有相同的显示名称的风险吗?如果我有两个名为“Joon的iPhone”的iPhone怎么办? - joon
无论设备名称如何,[[NSUUID UUID] UUIDString]都会产生不同的字符串。 - ChrisH
注意不要比较显示名称,因为默认情况下所有的iPad都被命名为“用户的iPad”。iPhone的问题较小,因为同一用户往往只有一个。但仍存在高碰撞风险。 - Niels Castle
显示剩余4条评论

5

如果有兴趣的话,我创建了一个名为MCSessionP2P的演示应用程序,展示了MCSession的自组网络功能。该应用程序在本地网络上进行广告推广,并通过编程方式连接到可用的对等方,建立点对点网络。感谢@ChrisH提供的邀请对等方比较哈希值的技巧。


对于目前正在使用 MCBrowserViewControllerMCAdvertiserAssistant 的人来说,这是一个不错的起点。 - ChrisH

4

我喜欢ChrisH的解决方案,它揭示了一个关键的见解:只有一方应该连接到另一方,而不是两者都连接。相互尝试连接会导致相互断开(虽然单向连接实际上在状态和通信方面是一种相互连接,这很反直觉,但也可以正常工作)。

然而,我认为比一个对等方邀请的更好的方法是双方都邀请,但只有一方接受。我现在使用这种方法,效果很好,因为双方都有机会通过邀请的参数向对方传递丰富的信息,而不必仅依靠foundPeer委托方法中可用的有限信息。

因此,我建议采用以下解决方案:

- (void)browser:(MCNearbyServiceBrowser *)browser foundPeer:(MCPeerID *)peerID withDiscoveryInfo:(NSDictionary *)info
{
    [self invitePeer:peerID];
}

- (void)advertiser:(MCNearbyServiceAdvertiser *)advertiser didReceiveInvitationFromPeer:(MCPeerID *)peerID withContext:(NSData *)context invitationHandler:(void (^)(BOOL accept, MCSession *session))invitationHandler
{
    NSDictionary *hugePackageOfInformation = [NSKeyedUnarchiver unarchiveObjectWithData:context];
    BOOL shouldAccept = ([hugePackageOfInformation.UUID.UUIDString compare:self.user.UUID.UUIDString] == NSOrderedDescending);

    invitationHandler(shouldAccept && ![self isPeerConnected:peerID], [self openSession]);
}

3

当设备同时尝试连接时,我遇到了同样的问题,但我不知道如何找到原因,因为我们没有出现任何关于MCSessionStateNotConnected的错误。

我们可以使用一些巧妙的方式来解决这个问题:在txt记录(发现信息)中添加一个时间戳 [[NSDate date] timeIntervalSince1970],记录应用启动的时间。谁先启动,就邀请其他人加入。

但我认为这不是正确的方法(如果应用程序同时启动,可能会出现问题... :))。我们需要找出原因。


更好的解决方案是通过邀请上下文参数传递信息。由于两个设备都可以很好地互相邀请,但只有一个设备可以接受,因此您可以使用此上下文在接收到邀请委托方法中进行区分,而不是在找到对等体委托方法中确定谁应该邀请。 - SG1
我认为@SG1的方法不太理想,因为它浪费带宽。 - bloudermilk

2
这是一个bug的结果,我已向苹果报告了此问题。我已在回答另一个问题时解释了如何修复它:为什么我的MCSession同行会随机断开连接? 我没有将这些问题标记为合并,因为虽然根本原因和解决方案相同,但两个问题描述了不同的问题。

这似乎是我所缺失的拼图中的一块。谢谢! - ChrisH
即使使用证书处理程序,我仍然遇到了断开连接的问题。 - jjxtra

1

保存对等体B的哈希值。使用定时器连续检查连接状态,如果未连接,请在每个给定时间段内尝试重新连接。


邀请Peer加入Session,并设置上下文和超时时间:yes - artud2000
我尝试过那个,但它只是向状态委托发送了PeerNotConnected。 - James Andrews
第一次查找设备时,您传递的服务类型是什么? - artud2000
不需要计时器。一旦连接成功,您将始终在状态更改时接收委托方法。如果在连接超时期间对等方未能连接,则尝试重新连接。@Ben,这是解决您问题的相同方法。 - SG1

0

0

看起来.notConnected消息是一个误报,因为设备仍在接收数据。因此,我手动更新了本地连接状态为.connected

很难从其他示例中分离出其他状态。因此,我为SwiftUI编写了一个最基本的MCSession示例,在这里:MultiPeer


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接