【问题标题】:Simple online web game crash eventually简单的网络游戏最终崩溃
【发布时间】:2017-01-03 17:17:24
【问题描述】:

背景: 我正在用 PHP、JavaScript 和 HTML 为 web 制作一个简单的游戏。玩家在屏幕上控制一个盒子的动作,并看到其他人拿着盒子飞来飞去。

我有以下文件,我通过托管公司上传到我的域:

  • index.html:带有一些按钮(例如,开始游戏)和框架(用于放入盒子)的文件。
  • server.php:从客户端接收消息的 PHP 脚本,对数据库执行读/写操作,从数据库回显(使用 echo)框到客户端。不回显消息来自的播放器框。
  • database.txt:一个 JSON 文本文件,包含玩家数据和下一个免费 ID 号。当为空时,它看起来像这样:{"players":[], "id": 1}玩家 包含具有 ID、位置和旋转等值的对象。
  • script.js:带有脚本的 JavaScript 文件,用于发送/接收消息、显示消息中的数据等。链接到 index.html。移动你的盒子。

截图,两名球员在移动:

问题:游戏总是崩溃。迟早。这就是发生的事情:

  1. 客户端从server.php接收玩家数据,一切正常。这可能持续 10 秒或长达几分钟。
  2. 数据开始不稳定,消息有时是 null 而不是实际数据。
  3. 收到的数据总是null。数据库文件现在是{"players":null,"id":5}。 ("id" 可以是任意数字,不一定是 5)。

数据流图片,从数据库打印玩家。两名球员。在此屏幕截图之前,有很多行包含有效数据。然后看到两条 null 消息。然后过了一会儿 null 永远。

我不完全确定问题出在哪里,但我猜这与我在server.php 中的读/写有关。我觉得很多玩家的移动使程序更容易崩溃。还有程序发送数据影响的频率。

代码片段 1: 这是来自 server.php 的代码,用于写入数据库。我有某种 semaphoreflock( ... ))来防止客户端同时读/写(导致错误)。我还有一个函数,read,和这个很相似。这里可能出现的问题:

  • 信号量不正确。
  • fopen() 的模式不正确。见PHP docsw 模式用于写入。标签 b 用于 “如果您在处理二进制文件时没有指定 'b' 标志,您的数据可能会遇到奇怪的问题......”
  • 因为我在写作功能中使用read() 而发生了什么奇怪的事情?

代码:

// Write $val to $obj in database JSON
function write($obj,$val){
   $content = read();
   $json = json_decode($content);
   $json->{$obj} = $val; // eg. $json->{'id'} = 5; 
   $myfile = fopen("database.txt", "wb") or die("Unable to open file!");
   if(flock($myfile, LOCK_EX|LOCK_NB)) {
     fwrite($myfile,json_encode($json));
     flock($myfile, LOCK_UN);
   }
   fclose($myfile);

}

代码片段 2: 这是我发送数据的代码。它通过setInterval() 调用。在script.js

// Send message to server.php, call callback with answer
function communicate(messageFunc,callback){
  var message = messageFunc();
  if (window.XMLHttpRequest) {
    var xmlhttp=new XMLHttpRequest();
  }
  xmlhttp.onreadystatechange= function() {
    if (this.readyState==4 && this.status==200) {
      callback(this.responseText);
    }
  }
  xmlhttp.open("GET","server.php?msg="+message,true);
  xmlhttp.send();
}

这是我接收数据的代码,在server.php:$receive = $_GET["msg"]

我目前的解决工作 这是我到目前为止所做的,但没有任何改变:

  • 将模式 b 添加到 fopen()
  • server.php 中添加了flock() 以读取/写入函数。
  • script.js 进行了很多修改,我会说它看起来/工作起来非常干净。
  • 检查memory_get_peak_usage(),并与托管公司核实内存限制。应该完全没有问题。
  • 查看了 PHP 垃圾收集和gc_enable()(我不知道为什么这会改变任何事情)。
  • 大量测试,查看数据流。
  • 哭了。

结论:这种类型的应用程序是 PHP 的用途吗?你觉得哪里不对?如果您想要我提供的更多代码/信息。非常感谢。

【问题讨论】:

    标签: javascript php server


    【解决方案1】:

    这是你问题的根源:

    $myfile = fopen("database.txt", "wb") or die("Unable to open file!");
    

    注意behavior of the w open mode(强调我的):

    仅供写作;将文件指针放在文件的开头并将文件截断为零长度。如果文件不存在,请尝试创建它。

    这发生在锁定文件之前。发生的情况是,在此 fopen() 调用和以下 flock() 调用之间,文件的内容长度为零,并且在此期间有一个阅读器出现并读取空文件。

    当您将空字符串解析为 JSON 时,为什么这不会导致 PHP 出错?因为json_decode() 是有缺陷的,当输入不是有效的JSON 时返回null 而不是抛出异常。没关系,字符串 "null" 是有效的 JSON——json_decode() 让您无法区分表示空值的有效输入和无效输入的情况。如果json_decode() 确实抛出了异常或触发了 PHP 错误(不要问我为什么 PHP 中需要两个错误信号机制),那么您可以开始调试以找出文件为空的原因,并且你现在可能已经解决了这个问题!

    ... 叹息 ...

    PHP 的“设计”让我头疼。但我离题了。

    要解决整个问题,请在成功获取锁后将打开模式更改为"cb"ftruncate($myfile, 0)

    注意c 模式的行为,它实际上特别提到了您正在使用的方法(强调我的):

    打开文件仅用于写入。如果文件不存在,则创建它。如果它存在,它既不会被截断(与 'w' 相反),也不会调用此函数失败(如 'x' 的情况)。文件指针位于文件的开头。 如果希望在尝试修改文件之前获得建议锁(请参阅flock()),这可能很有用,因为使用 'w' 可能会在获得锁之前截断文件(如果截断是需要,ftruncate() 可以在请求锁定后使用)。

    【讨论】:

    • 很好的答案,谢谢!它似乎已经解决了这个问题!但是,有时我仍然会收到一些 null 消息,尽管最终不会像以前那样永远存在。我改变了你的建议,在我的read中添加了模式c+b,并且在我关闭文件后也将unlocking移到了。
    • @JakeTheSnake 如果你先fclose(),解锁应该会失败,因为文件句柄现在无效。相反,请考虑在解锁之前添加fflush() 调用。可能发生的情况是写入的数据正在被缓冲,因此仍有一个窗口(在释放锁和关闭文件之间)数据尚未写入磁盘。因此,您的解决方案将工作如果在关闭文件后解锁文件。在释放锁之前刷新数据是有保证的解决方案;在另一个进程获取锁之前,数据将在磁盘上。
    • @JakeTheSnake 请注意,在 PHP 5.3.2 之前,关闭文件会隐式释放所有锁,因此如果您使用的是旧的 PHP 版本,它可能对您有用,因为 fclose()无论如何,调用都在释放锁,而释放它的flock() 调用失败了——但这没关系。我会使用 flush-unlock-close 模式,就像您升级到或超过 PHP 5.3.2 一样,您会遇到奇怪的锁争用问题,因为即使在文件关闭后也会保持锁。
    • 进一步说明:我认为阅读你会想要模式rbc+b 在阅读案例中并没有真正为您做任何事情。事实上,此时创建文件可能是错误。如果文件在读取的情况下不存在,您可以立即失败,而不是创建文件然后发现它是空的。
    • 非常好,真的。我还注意到一件事,LOCK_NB 可能使用错误。我在关于 flock 的 PHP 文档上读到了这篇文章:“如果您不希望 flock() 阻塞,也可以将 LOCK_NB 作为位掩码添加到上述操作之一 同时锁定。”这听起来很奇怪,而且似乎这是最后一个要修复的错误。有用!再次感谢。
    猜你喜欢
    • 2015-06-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多