Game Center GKMatch GKSendData Надежный пакет потерян
Я уже довольно давно успешно использую 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];
}
Что я вижу, так это то, что периодически (возможно, 1 из 100 сообщений) отправляется без ошибок из метода 'sendDataToAllPlayers', но принимающее устройство никогда не использует метод didReceiveData. Насколько я понимаю, использование GKSendDataReliable должно отправлять сообщения, а затем не отправлять другое, пока не получит подтверждение. Сообщения не принимаются, но новые сообщения отправляются с того же устройства.
Метод отправки возвращает 'YES', и ошибка равна nil, но didReceiveData никогда не удаляется...!
Кто-нибудь когда-нибудь видел это? У кого-нибудь есть идеи, что это может быть? Я не знаю, что еще я мог сделать, чтобы отладить это.
2 ответа
Я подтверждаю ошибку. Я сделал пример проекта, последовательно воспроизводящего проблему: https://github.com/rabovik/GKMatchPacketLostExample. Я тестировал его на слабом интернет-соединении (iPad 3 с Wi-Fi и iPhone 4S с EDGE; оба на iOS 6.1.3), и некоторые пакеты регулярно терялись без каких-либо ошибок из Game Center API. Более того, иногда устройство перестает получать какие-либо данные, в то время как другое все еще отправляет и получает сообщения успешно.
Поэтому, если мы уверены, что ошибка существует, единственный возможный обходной путь - добавить дополнительный транспортный уровень для действительно надежной доставки.
Для этой цели я написал простую библиотеку: https://github.com/rabovik/RoUTP. Он сохраняет все отправленные сообщения до подтверждения для каждого полученного, повторно отправляет потерянные и буферизирует полученные сообщения в случае нарушения последовательности. В моих тестах комбинация "RoUTP + GKMatchSendDataUnreliable" работает даже лучше, чем "RoUTP + GKMatchSendDataReliable" (и, конечно, лучше, чем чистый GKMatchSendDataReliable, который не очень надежен).
[Редактировать: RoUTP больше не работает должным образом в iOS9]
Вчера я провел некоторое тестирование на границе моего Wi-Fi, где происходила потеря пакетов. Что происходит, когда пакеты теряются с помощью GKMatchSendDataReliable, проигрыватель внезапно отключается от сеанса GKMatch. match:player:didChangeState вызывается с помощью GKPlayerStateDisconnected, а идентификатор игрока удаляется из словаря playerIDs. Это происходит с небольшой потерей пакета. Я все еще могу просматривать Интернет из этого соединения, например.
Теперь, если я переключаюсь на ненадежную отправку пакетов, то match: player: didChangeState никогда не срабатывает, и сопоставление продолжается без проблем (кроме потери случайного пакета, который может быть важен). Он отключится только в том случае, если потеря пакета станет существенной. Теперь именно здесь удобна библиотека RoUTP Яна, поскольку мы можем отслеживать и повторять эти важные сообщения, не отключая наших игроков в случае небольшой потери пакетов.
Кроме того, отправка данных с использованием GKMatchSendDataReliable вернет YES, только если сообщение было успешно поставлено в очередь для доставки. Это не говорит вам, было ли сообщение успешно доставлено. Как это могло? Это возвращается сразу.