背景 我正在用PHP,JavaScript和HTML为网络制作一个简单的游戏。玩家控制屏幕上框的移动,并看到其他人用他们的盒子飞来飞去。
我有以下文件,我通过托管公司上传到我的域名:
index.html
:带有一些按钮(例如,开始游戏)和框架(用于放入框)的文件。 server.php
:从客户端接收消息,对数据库执行读/写操作,从数据库回显(使用echo
)框到客户端的PHP脚本。不回应消息来自的播放器的框。 database.txt
:包含玩家数据和下一个免费ID号的JSON文本文件。如果为空,它看起来像这样:{"players":[], "id": 1}
。 玩家包含具有ID,位置和旋转等值的对象。 script.js
:包含脚本的JavaScript文件,用于发送/接收消息,显示消息中的数据等。链接到index.html
。移动你的盒子。 问题:游戏总是崩溃。迟早。这就是:
server.php
收到玩家数据,一切都很好。这可能是10秒或几分钟。 {"players":null,"id":5}
。 (“id”可以是任何数字,不一定是5)。 数据流的图片,从数据库打印播放器。两名球员。在此截图之前,许多行都包含有效数据。然后看到两个 null 消息。然后一段时间 null 永远。
我不完全确定问题出在哪里,但我猜它与server.php
中的读/写有关。我觉得很多玩家的动作让程序更容易崩溃。此外,程序发送数据的频率也是如此。
代码片段1:这是来自server.php
的代码,它写入数据库。我有某种信号量(flock( ... )
)来防止客户端同时读/写(导致错误)。我有另一个函数, read ,这与此非常相似。这里可能出现的问题:
fopen()
的模式不正确。见PHP docs。模式 w 用于写入。标签 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);
}
Code Piece 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"]
中接收数据的代码。
我目前解决的工作 这是我到目前为止所做的,但没有任何改变:
fopen()
。 flock()
中读取/写入函数添加了server.php
。 script.js
进行大量修改,我会说它外观/工作非常干净。 memory_get_peak_usage()
,并与托管公司核实内存限制。应该没问题。 gc_enable()
(我不知道为什么会改变任何东西)。 结论:这种类型的应用程序是PHP的用途吗?你觉得怎么了?如果您想要更多我提供的代码/信息。非常感谢你。
答案 0 :(得分: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()
。