问题 游戏中心GKMatch GKSendDataReliable数据包丢失


我在应用程序中成功使用GKMatch已经有一段时间了。我一直在追逐,并偶尔停止游戏,并将其跟踪到正在发送但未收到的数据包。这种情况只是偶尔发生,但我似乎无法追查其发生的原因。

所有消息都使用GKSendDataReliable发送。

记录显示数据包正在从一台设备成功发送,但从未在目标设备上接收。

//Code sample of sending method....
//self.model.match is a GKMatch instance    
-(BOOL) sendDataToAllPlayers:(NSData *)data error:(NSError **)error {
        [self.model.debugger addToLog:@"GKManager - sending data"];
        return [self.model.match sendDataToAllPlayers:data withDataMode:GKSendDataReliable error:error];
    }

...

//Code sample of receiving method....
// The match received data sent from the player.
-(void)match:(GKMatch *)match didReceiveData:(NSData *)data fromPlayer:(NSString *)playerID {
    [self.model.debugger addToLog:@"GKManager - received data"];
    [super didReceiveData:data fromPlayer:playerID];
}

我所看到的是周期性地(可能是100个消息中的1个)在没有错误的情况下从'sendDataToAllPlayers'方法发送,但是接收设备从不命中'didReceiveData'方法。我的理解是,使用GKSendDataReliable应该发送消息,然后在收到确认之前不会发送另一个消息。未收到消息,但从同一设备发送新消息。

发送方法返回'YES'并且错误是nil,但didReceiveData永远不会被命中......!

有没有人见过这个?有没有人有任何想法这可能是什么?我不知道我还能做些什么来调试这个。


1022
2018-06-07 15:35


起源

我的用户还抱怨在游戏过程中可能会意外丢失某些数据。但是我仍然无法自己重现这个错误。你有一个示例项目,这个bug一直可以重现吗?如果是的话,请你分享一下(可能在github上)?谢谢。 - Yan
我的项目非常大(目前商店中的活动应用程序),但我会尝试将它的较小版本放在一起,以确定它是否始终可重现。 - Jonathan Hebert
我遇到了同样的问题,特别是当其中一台设备上的互联网连接功能较弱时。 GKSendDataReliable在收到确认之前是否停止发送消息是真的吗? - jyek
你有没有为此提交错误报告?这似乎是根本性破坏的功能,应该影响任何使用实时匹配的开发人员。 - Shaun Budhram
我也证实了这一点。苹果真的非常惊讶(更不用说令人失望了)。可靠模式的重点是连接应该缓冲并延迟,直到它可以发送,而不仅仅是丢弃消息。 - theLastNightTrain


答案:


我确认了这个bug。 我做了一个示例项目,一直在重现这个问题: https://github.com/rabovik/GKMatchPacketLostExample。我在一个弱的互联网连接上测试它(带有Wi-Fi的iPad 3和带有EDGE的iPhone 4S;两者都在iOS 6.1.3上),并且一些数据包经常丢失而没有任何来自Game Center API的错误。此外,有时设备停止接收任何数据,而另一个仍然成功发送和接收消息。

因此,如果我们确定存在错误,唯一可行的解​​决方法是添加额外的传输层以实现真正可靠的传输。

我为此编写了一个简单的lib: https://github.com/rabovik/RoUTP。它保存所有已发送的消息,直到确认每个接收到的消息,重新发送丢失并缓冲接收到的消息,以防序列中断。 在我的测试组合中,“RoUTP + GKMatchSendDataUnreliable”甚至比“RoUTP + GKMatchSendDataReliable”更好(当然比纯粹的GKMatchSendDataReliable更好,但这并不可靠)。


8
2017-07-06 13:13



现在,使用iOS 9,RoUTP似乎已被破坏。如果我使用它发送数据,并非所有设备都会收到数据。如果我使用可靠或不可靠的运输无关紧要。如果我禁用RoUTP一切正常。 - Robert Wasmann


[编辑:在iOS9中RoUTP似乎不再正常工作]

我昨天在wifi的范围边做了一些测试,丢失了。当使用GKMatchSendDataReliable丢失数据包时,播放器突然与GKMatch会话断开连接。 match:player:使用GKPlayerStateDisconnected调用didChangeState,并从playerIDs字典中删除播放器的ID。这种情况只会发生轻微丢包。例如,我仍然可以通过此连接浏览互联网。

现在,如果我切换到不可靠的发送数据包,那么匹配:player:didChangeState永远不会触发并且匹配继续没有问题(除了丢失偶尔可能很重要的数据包)。只有在数据包丢失变得很大时才会断开连接。现在这就是Yan的RoUTP库很方便的地方,因为我们可以跟踪并重试这些重要的消息,而不会让玩家在遇到轻微丢包时断开连接。

此外,如果消息已成功排队等待传送,则使用GKMatchSendDataReliable发送的数据将仅返回YES。它不会告诉您邮件是否已成功传递。怎么可能?它马上回来了。


1
2017-11-10 15:16



嘿,我一直在做一个非常复杂的rts游戏,我已经注意到你正在描述的问题。你推荐我试试RoUTP库吗? - Epic Byte
它肯定有效,但它非常基础。例如,一旦开始使用它,所有数据包都必须通过RoUTP发送。它将继续尝试永远传递数据包。这可能会导致一连串的延迟数据包同时出现。我可能会考虑使用完整的客户端服务器而不是点对点和放弃RoUTP。如果客户端丢包并断开连接,那么这可能只是让其他客户端保持游戏正常的理想结果,而不是让任何人因为数据包激增而在任何人可以做出反应之前杀死其他玩家的场景。 - Robert Wasmann
它也没有说明谁删除了该信息被丢弃的数据包。所以你必须使用char数组将playerID编码到消息中。所有玩家都收到所有消息,所以如果你去客户端服务器那么它就不理想了。也就是说,您无法向特定玩家发送消息。仅限所有玩家,然后让玩家忽略不适合他们的消息。 - Robert Wasmann
嘿谢谢你的回应。所以我最初发送数据包是可靠的,但由于Game Center处理与可靠数据包断开连接的方式,我需要尝试别的东西,因为即使最小的超时也会断开用户。所以我最终做的是在发送消息上编写我自己的层不可靠并自己管理可靠队列。这允许我将某些消息优先于其他消息,并在不再需要时手动丢弃数据包(即,如果花费的时间太长)。到目前为止,我用这种方法取得了成功。并且没有发生断开连接。 - Epic Byte
多年来,我一直在经历“可靠”数据包的丢失和自发的-didChangeState断开连接。我从未想过可靠的数据包丢失会触发断开连接,因此我通过将所有数据更改为不可靠来进行快速测试。不用找了。我仍然在3或4人游戏中获得-didChangeState断开连接。但是,在2场比赛中似乎没有发生。 - BGreenstone