PHP: игровой цикл (потоки или сортировка)

Я пишу 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 на самом деле нет многопоточности. Каков наилучший способ справиться с этим?


person Zeno    schedule 30.11.2010    source источник
comment
я не думаю, что PHP - лучший выбор для клиентской технологии. Рассмотрим Python, Java или .net.   -  person Andrey    schedule 30.11.2010
comment
Да я знаю, я не настроен на это. Клиент уже написан на Java и я его переписываю. Пытаюсь определиться с языком. У вас есть примеры чего-то подобного в Python?   -  person Zeno    schedule 30.11.2010


Ответы (7)


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

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

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

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

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

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

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

Если вы отметили все эти флажки, то PHP ЕЩЕ В ИГРЕ! Иначе. Не беспокойтесь.

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

УРОВЕНЬ ВУДУ

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

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

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

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

person DampeS8N    schedule 30.11.2010

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

Чтобы проверить сокет без блокировки, вам нужно использовать неблокирующий ввод-вывод, которого вы можете достичь с помощью socket_set_nonblock() или socket_recv() с НЕ ЖДИТЕ флаг.

person AndreKR    schedule 30.11.2010

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

person Stephen    schedule 30.11.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;
    }
}

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

person aib    schedule 30.11.2010

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

person KingCrunch    schedule 30.11.2010

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

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

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

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

person Jacek Prucia    schedule 30.11.2010

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

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

person Ariejan    schedule 30.11.2010