PHP: игровой цикл (темы или сортировка) - PullRequest
1 голос
/ 30 ноября 2010

Я пишу код PHP, чтобы быть игровым клиентом. Использует сокет; socket_create, за которым следует socket_connect, а затем socket_read. Он работает нормально, но проблема в том, что сервер может отправить пакет в любое время, что означает, что socket_read должен постоянно происходить в «игровом цикле». Так что-то вроде этого:

<?php 
$reply = ""; 
do { 
     $recv = ""; 
     $recv = socket_read($socket, '1400'); 
     if($recv != "") { 
         $reply .= $recv; 
     } 
} while($recv != ""); 

echo($reply); 
?>

Не работает, потому что он застрял в цикле (сервер не прерывает соединение до тех пор, пока игра не будет завершена клиентом), а PHP-код должен обрабатывать пакет, как только он поступит.

Так что в PHP нет потоков. Какой лучший способ справиться с этим?

Ответы [ 7 ]

1 голос
/ 30 ноября 2010

В принципе, любая программная платформа столкнется с этой проблемой.Большинство, как вы уже поняли, решают это с помощью потоков.Хотя многопоточность возможна в PHP.Требуется MAJORHAXXX.Например, запуск php-потока из командной строки из php.

Это действительно не значит, что он идеален.

Однако есть и другие способы обойти это.

Но сначала вам нужно проверить ВСЕ метки в этом списке:

[] - моей игре не нужно постоянно проверять сервер, например, на предмет местоположения игрока или сложные движения.Все, что выходит за пределы уровня передачи данных и скорости обновления в чате, должно быть оставлено не отмеченным.

[] - СЕРВЕР не должен ничего рассказывать о моей игре.Для клиента вполне приемлемо запрашивать все, что ему нужно, возможно, раз в секунду или, что лучше, раз в минуту.

[] - Моя игра не нуждается в постоянной симуляции работы сложного мира.на сервере дольше, чем требуется для выполнения запроса.Отслеживание чата - это одно, а внесение изменений в физику и графику - другое.

Если вы отметили все эти флажки, то PHP все еще в игре!Иначе.Не беспокойтесь.

По сути, я говорю здесь о том, что PHP отлично подходит для игр, которые на самом деле не являются многопользовательскими, и являются пошаговыми или, по крайней мере, не очень интерактивными.Но как только вы продолжаете работать без игрока, PHP падает на ноги.

УРОВЕНЬ ВУДОО

Но если вы просто ДОЛЖНЫ сделать это.Существуют способы обойти это.

A - Создайте PHP-демон, управляющий вашим миром, перенаправьте весь остальной трафик в файл запроса получения или установки, который взаимодействует с базой данных.Таким образом, вы можете запросить получение состояния игрового мира или установить значение, которое выполнил игрок.Все остальные вещи, связанные с игровым миром, могут обрабатываться демоном, а сама игра происходит в базе данных.

B - Используйте cron, а не Daemon.(опасно, но мы уже установили, что вы рискуете, верно?)

C - ПОПРОБУЙТЕ только Демон и слушая сокеты, затем отправляя потоки (через exec ()) для ответа.Вроде как идея АндреКР выше, только вам не нужно спать.Проблема здесь в том, что вы почти всегда будете в конечном итоге пропускать вещи или иным образом отрезаться.И все это может взорваться, если Демон побежит как-то дважды ..

1 голос
/ 30 ноября 2010

Можно сделать, но я согласен с @Andrey и @ DampeS8N, не лучшим выбором.Если вы не можете этого сделать, проверьте эту книгу: Вы хотите сделать ЧТО с PHP?

0 голосов
/ 30 ноября 2010

Вместо написания этого вонючего блокирующего цикла опроса, обратите внимание на некоторую систему событий, основанную на паттерне реактора, такую ​​как Python Twisted или Ruby EventMachine.

Я считаю, что разновидность PHP - это вызов PHP-MIO: http://thethoughtlab.blogspot.com/2007/04/non-blocking-io-with-php-mio.html

0 голосов
/ 30 ноября 2010

Все, что вам нужно сделать, это использовать socket_select() функцию:

http://php.net/manual/en/function.socket-select.php

Он переведет ваш скрипт в спящий режим и разбудит его, когда в сокете будут данные для чтения. Это намного эффективнее, чем периодический сон / чтение, cron-скрипты и все другие предлагаемые решения.

@ aib сделал правильное замечание. Сервер может отправить полное «игровое сообщение», разделенное на несколько пакетов. Не ожидайте получить все ваши данные в единственном исключении блока кода после возврата socket_select().

0 голосов
/ 30 ноября 2010

Реализации TCP имеют тенденцию фрагментировать и объединять сообщения;невозможно сказать, сколько данных или сколько фрагментов сообщений вернет сокет.Вам необходимо знать, где заканчивается сообщение и начинается новое (что может происходить несколько раз в данных, возвращаемых за одно чтение).Несколько простых решений:

  • Используйте какой-нибудь разделитель.Завершите каждое сообщение '\ 0'.
  • Отправьте размер сообщения вместе с сообщением.Начинайте каждое сообщение с «Content-length: 42 \ n» или двумя байтами размера (0x00 0x42).
  • Используйте XML.<message> запускается, а </message> завершает сообщение.

Анализатор XML в PHP не любит неполные XML-файлы, хотя третий вариант отсутствует, если вы не хотите сопоставлять начальный и конечный теги вручную.Используйте первую опцию, если протокол основан на ASCII, вторую, если он двоичный, и третью, если это уже XML.

Теперь запомните, что вы можете получать любое количество сообщений в пакете.В наиболее сложном случае может быть конец более раннего сообщения, за которым следует ряд полных сообщений и начало еще одного сообщения в одном пакете.

Полное решение будет следующим:

while (connected) {
    while (messages in buffer < 1) {
        read from socket;
        add to buffer;
    }

    while (messages in buffer > 0) {
        extract message from buffer;
        process message;
    }
}

... хотя это асинхронный цикл обработки сообщений.В качестве упражнения я оставлю синхронную реализацию «если есть сообщение, верните его; иначе подождите».(Подсказка: вам понадобится класс для создания и буферизации сообщений.)

0 голосов
/ 30 ноября 2010

Если вы действительно хотите это сделать, вам нужно поспать некоторое время, проверить розетку, снова заснуть, проверить розетку ...

Чтобы проверить розетку без блокировкивам нужно использовать неблокирующий ввод / вывод, которого вы можете достичь с помощью socket_set_nonblock() или socket_recv(), который имеет флаг DONTWAIT.

0 голосов
/ 30 ноября 2010

В PHP нет многопоточности, поэтому вам стоит подумать об использовании более подходящего языка (как Андрей упоминал в своем комментарии).

...