PHP:游戏循环(线程或排序)

时间:2010-11-30 15:44:04

标签: php multithreading sockets game-loop

我正在编写PHP代码作为游戏客户端。它使用socket; 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 个答案:

答案 0 :(得分:1)

可以做到,但我同意@Andrey和@ DampeS8N,不是最好的选择。如果你已经死定了,请查看这本书:You want to do WHAT with PHP?

答案 1 :(得分:1)

基本上任何软件平台都会遇到这个问题。大多数,正如你已经想到的那样,用线程解决它。虽然在PHP中可以进行线程化。它需要MAJORHAXXX。比如从php中启动命令行php线程。

它真的不是最理想的。

但是,还有其他方法可以解决这个问题。

但您需要先检查此列表中的所有标记:

[] - 我的游戏不需要经常检查服务器,例如玩家位置或复杂的动作。超出聊天室级别的数据传输和更新速率的任何内容都应该不会选中此框。

[] - 我的游戏不需要被服务器告知任何事情。客户要求任何需要的东西是完全可以接受的,可能每秒一次或更好一次。

[] - 我的游戏不需要对服务器上运行的复杂世界进行持续模拟的时间长于完成请求所需的时间。跟踪聊天是一回事,进行物理和图形修改是另一回事。

如果您选中了所有这些框,那么PHP仍然在游戏中!除此以外。不要打扰。

基本上,我在这里所说的是PHP非常适合那些不是真正的多人游戏,而且它们基于回合制,或者至少不是非常具有互动性。但是一旦你不得不让玩家继续前进,PHP就会落空。

VOODOO LEVEL

但如果你只是必须这样做。有办法绕过它。

A - 创建一个运行您的世界的PHP守护程序,将所有其他流量传输到与数据库交互的getter或setter请求文件。因此,您可以请求获取游戏世界状态,或设置玩家执行的值。所有其他与游戏世界相关的东西都可以由守护进程处理,游戏本身也可以在数据库中进行。

B - 使用cron,而不是守护进程。 (危险,但我们已经确定你是冒险者,对吗?)

C - 只调用守护进程并侦听套接字,然后发送线程(通过exec())进行响应。有点像上面的AndreKR的想法,只有你不需要睡觉。这里的问题是你几乎总会错过任何东西或以其他方式被切断。如果守护进程以某种方式运行两次,整个事情可能会爆炸......

答案 2 :(得分:0)

PHP没有多线程,所以你应该考虑使用更合适的语言(比如评论中提到的Andrey)。

答案 3 :(得分:0)

如果真的想要这样做,你必须睡一段时间,检查插座,再次睡觉,检查插座......

要检查套接字而不阻塞,您需要使用非阻塞I / O,您可以使用具有DONTWAIT标志的socket_set_nonblock()socket_recv()来实现。

答案 4 :(得分:0)

TCP实现倾向于分段和加入消息;没有人知道套接字会收到多少数据或多少消息片段。您需要知道消息的结束位置以及新消息的开始位置(单次读取返回的数据可能会多次发生)。一些简单的解决方案:

  • 使用某种分隔符。以'\ 0'结束每条消息。
  • 发送邮件大小和邮件。使用“Content-length:42 \ n”或两个大小的字节(0x00 0x42)启动每条消息。
  • 使用XML。 <message>开始,</message>结束消息。

PHP的XML解析器不喜欢不完整的XML,但第三个选项是out,除非你想手动匹配起始和结束标记。如果协议基于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;
    }
}

...虽然这是一个异步消息循环。我将留下“如果有可用消息,则将其返回;否则,等待一个”同步实现作为练习。 (提示:你需要一个类来构建和缓冲消息。)

答案 5 :(得分:0)

您所要做的就是使用socket_select()功能:

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

当要读取的套接字上有数据时,它会让您的脚本进入休眠状态并将其唤醒。定期睡眠/阅读,cron脚本和所有其他提议的解决方案更有效率。

@aib提出了一个有效的观点。服务器可能会发送一个完整的“游戏消息”,分为几个数据包。在socket_select()返回后,不要期望在代码块中获得所有数据。

答案 6 :(得分:0)

不是编写这个臭的阻塞轮询循环,而是检查一些基于反应器模式的事件系统,如Python Twisted或Ruby EventMachine。

我相信PHP的味道是调用PHP-MIO:http://thethoughtlab.blogspot.com/2007/04/non-blocking-io-with-php-mio.html

相关问题