Довідка щодо обміну повідомленнями клієнт-сервер та синхронізація годин


10

Я роблю швидку швидку фізичну гру, яка є хокеєм настільним. З двома молотками і однією шайбою. Гра працює на iphone / ipad, і я роблю багатокористувацьку частину через GameCenter.

Так працює мережева система. Клієнт, який позначає зірочку, буде вирішений як сервер, а той, хто приймає запит на відповідність, - це клієнт.

У «сервера» працює фізика, і відповідь є негайною, і клієнт також працює з фізикою, так що між обміном повідомленнями він виглядає гладко. Що я роблю як сервер, це те, що я надсилаю клієнтові свою швидкість шайби та мою позицію, і клієнт коригує свою швидкість шайби / позицію, пов'язану з сервером, щоб підтримувати її синхронізацію. В іншому випадку фізика десинхронізується, і вона прикручується.

Коли затримка в мережі хороша, нижче 100 мс результати дуже хороші, я отримав плавну гру на клієнті, і дивна поведінка мінімальна. Проблема трапляється, коли відставання становить від 150 до 200 мс. У такому випадку трапляється, що шайба мого клієнта вже потрапила в край і перевернув напрямок, але він отримує повідомлення про затримку від сервера і трохи відступає, викликаючи дивне відчуття поведінки м'яча.

Я прочитав кілька речей про це:

Приклад мережі Pong

Клацніть Приклад синхронізації

Вікіпедія на синхронізації годин

Отже, як я можу це вирішити? Наскільки я прочитав найкращий варіант, який у мене є, - це зробити синхронізацію годин на сервері / клієнті з часовою позначкою, щоб, коли я отримую повідомлення про затримку, пов'язані з моїм годинником, я просто ігнорую тоді і дозволяю клієнтам моделювання робити робота. Чи згодні ви з цим? А оскільки я надсилаю недостовірні дані (UDP), я можу отримати повідомлення із затримкою чи повідомлення не в порядку.

Якщо це найкращий підхід, як я здійснюю синхронізацію годин. Я прочитав кроки, як це зробити, але я не зовсім зрозумів це.

Там сказано, що:

  1. Клієнт маркує поточний місцевий час на пакеті "запит на час" і відправляє на сервер.
  2. Після отримання сервером сервер маркує час сервера і повертається
  3. Після отримання клієнтом клієнт віднімає поточний час від відправленого часу і ділиться на два, щоб обчислити затримку. Він віднімає поточний час від часу сервера, щоб визначити дельту часу клієнт-сервер, і додає з половиною затримки, щоб отримати правильну дельту годинника. (Поки що цей альготим дуже схожий на SNTP)
  4. Клієнт повторює кроки від 1 до 3 п’ять і більше разів, кожен раз призупиняючи кілька секунд. Інший трафік може бути дозволений у проміжний час, але його слід мінімізувати для найкращих результатів Результати надходжень пакетів накопичуються та сортуються за найменшою затримкою до найвищої латентності. Середня затримка визначається шляхом вибору зразка середньої точки з цього упорядкованого списку.
  5. Усі зразки вище приблизно 1 стандартного відхилення від медіани відкидаються, а решта зразків усереднюються за допомогою середнього арифметичного.

Дотримуючись цього прикладу, я мав би це:

Давайте робити вигляд, що гра завантажена, і час мого клієнта зараз 0, тож я надсилаю на сервер, що мій час 0.

Повідомлення займає 150 мс, щоб дістатися до сервера, але годинник сервера вже почався і на 1 секунду випереджає клієнта. Коли сервер отримає повідомлення, час буде: 1,15 і відправить цей час клієнту, чи добре ми? Давайте робити вигляд, що наше відставання постійне в 150 мс.

Тепер клієнт отримує час 1,15 і віднімає поточний час від відправленого часу і ділиться на два, щоб обчислити затримку. Що становить: 0,3 - 0 = 0,3 / 2 -> 150 мс.

Він віднімає поточний час від часу сервера, щоб визначити дельту часу клієнт-сервер, і додає на половину затримки, щоб отримати правильну дельту годинника:
Час клієнта: 0,3 Час сервера 1,15
0,3 - 1,15 = .85 + затримка (.15) = 1

Як це синхронізується? Що я пропускаю?

Я вперше про багатокористувацький та мережевий досвід, тому я трохи розгублений.

Дякую.


Хороше питання. Чи можете ви правильно: 0,3 - 1,15 бути 1,15 - 0,3?
Ciaran

Відповіді:


12

Розміщений алгоритм був правильним, але у вашому прикладі ви забуваєте про час, який потрібно, щоб серверний пакет потрапив до клієнта, так:

Server time: 1
Client time: 0
Client sends 0 to server

... 150ms to get to server  (ping is 300! not 150ms in this case. Ping is round-trip)

Server time: 1.15
Client time: 0.15
Server receives packet and sends client 1.15

... 150ms to get back to client

Server time: 1.30
Client time: 0.30
Client receives 1.15 from server

Як ви бачите, якби клієнт змінив годинник на 1,15, він би відстав від сервера на 0,15, тому вам доведеться налаштувати на Ping (він же час туди і назад [RTT]). Ось повний розрахунок дельти часу, виконаний протягом багатьох кроків:

Server Time - Current Time + Ping / 2
= Server Time - Current Time + (Current Time - First Packet Time) / 2
= 1.15 (Perceived, not actual!) - 0.30 + (0.30 - 0.00) / 2
= 1.00

Це дає нам правильний час дельти 1,00 секунди


Я отримую його, тому годинник мого клієнта становить 1,00, а сервер - 1,30 Після того, як я отримаю повідомлення від сервера, чи потрібно мені додати ping, щоб перевірити, чи є пізнім повідомленням чи щось? Інша справа, що, якщо затримка зміниться, чи потрібно мені постійно робити ці кальку?
gmemario

Час дельти в 1:00 повинен бути доданий до поточного годинника, щоб зрівняти час клієнта з сервером. Затримка завжди змінюється, кожен пакет матиме дещо інший RTT. За короткий проміжок часу, як в грі, я б просто синхронізував цей раз лише один раз, клієнт буде досить добре здогадуватися про час сервера, і обидва годинники повинні рухатися вперед приблизно з однаковим темпом. Єдиним винятком буде, якщо ви отримали пакет, який, здається, є з майбутнього. У цьому випадку є два рішення: 1) Зробити нову синхронізацію часу. 2) Заздалегідь встановіть свій годинник, щоб він відповідав майбутнім годинникам
Джон Макдональд

Гаразд, давайте подивимось на цю ситуацію, щоб переконатися, що я її отримав: Час сервера: 1s Час клієнта: 0s 150ms, щоб дістатися до сервера Час сервера: 1.15s Час клієнта: 0.15s Сервер посилає клієнту 1,15s 200ms, щоб дістатися до клієнта Отже, мій пінг зараз становить 350 мс. Це правильно? Час роботи сервера: 1,35 Клієнтський час: 0,35 Виконання калькуляцій: 1,15 - .35 + (0,35 - 0,00) / 2 Тепер мій deltaTime = 0,975 Якщо я додати до мого часу дельти 0,975, я отримаю: 1,325, що є десинхронізацією. Це правильно також?
gmemario

@Gilson, це правильно. Якщо затримка для двох пакетів різна (майже гарантована), розрахунок дельти часу клієнта не буде ідеальним. Немає способу, щоб клієнт коли-небудь був ідеальним. В алгоритмі, який ви описали у запитанні, час дельти обчислювались декілька разів, і існував метод відбору та усереднення цих результатів. Багато методів синхронізації роблять щось подібне, але вони зазвичай для критичних для бізнесу та довготривалих серверів, як для бірж. Для точності в часі, яку ви потребуєте для короткої гри, я думаю, що це буде надмірно.
Джон Макдональд

@Gilson, Поки клієнт має розумну оцінку часу сервера і обидва годинники рухаються вперед приблизно з однаковою швидкістю, ви не повинні помічати жодних проблем. Коли клієнт або сервер надішле дію в іншу сторону, він надішле свій місцевий час. На стороні, що приймає, повідомлення завжди має бути в минулому і його потрібно інтерпретувати як минулу подію. Це означає, що вам потрібна вся інформація в пакеті, наприклад розташування, швидкість (величина + напрямок) та час. Тоді ви можете розмістити шайбу туди-сюди і перемістити шайбу з минулого в сучасне. Я в чаті btw
Джон Макдональд
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.