7

问题结束时的解决方案

我正在编写一个 PHP 应用程序,它向服务器发送消息,然后使用stream_get_contents. 我以相同的方式与 android 应用程序中的同一台服务器通信。android 应用程序工作正常并且响应迅速,但是当从服务器读取响应时 PHP 挂起。

在下面的代码示例中,我设置了一个 5 字节的微小缓冲区大小来测试一个理论。如果我删除此缓冲区大小,它会挂起,但是对于 5 字节大小,它只会在最后一次通过循环时挂起:

stream_set_timeout($this->socket, 10); //10 seconds read timeout

while (!feof($this->socket)) {
    $breakOut = false;

    echo 'Reading response'.time().'<br/>';
    $data = stream_get_contents($this->socket, 5);
    echo 'Read response'.time().'<br/>';

    if ($data === false) {
        $this->latestErrStr = "Timed out waiting for a response.";
        return false;
    } else {
        $index = strpos($data, chr(3));

        if ($index !== FALSE){
            $breakOut = true;
            $data = substr($data, 0, $index);
        }

        $response .= $data;
    }

    $stream_meta_data = stream_get_meta_data($this->socket);

    //If we have no EOF marker then break if there are no bytes left to read
    if($breakOut || $stream_meta_data['unread_bytes'] <= 0) {
        break;
    }
}

输出如下:

Reading response1387463602
Read response1387463603
Reading response1387463603
Read response1387463603
Reading response1387463603
Read response1387463603
Reading response1387463603
Read response1387463603
Reading response1387463603
Read response1387463603
Reading response1387463603
Read response1387463603
Reading response1387463603
Read response1387463603
Reading response1387463603
Read response1387463603
Reading response1387463603
Read response1387463603
Reading response1387463603
Read response1387463603
Reading response1387463603
Read response1387463603
Reading response1387463603
Read response1387463603
Reading response1387463603
Read response1387463623

如您所见,最后两行之间有 10 秒的延迟,但其他行之间没有明显的延迟。

另外,为了您的信息,我使用 ETX 标记 (3) 来表示消息的结束,所以如果我点击它,我也会停止,而不仅仅是流的结束。

难道我做错了什么?有没有更好的方法来做到这一点?

提前致谢...

编辑:为了清楚起见,上面的代码只需要一个消息响应。它不关心收到 ETX 字节后返回的任何数据。

Edit2:现在已经看到挂起长达 40 秒。它似乎没有固定为 10 秒,但奇怪的是,每次似乎都是不错的整数。

解决方案(感谢chathux)

stream_get_contents($stream, $bytes)将阻塞,直到它接收到$bytes字节或超时到期。这意味着我的代码即将结束并尝试读取 5 个字节(不存在),因此它在放弃之前等待了 10 秒。

我知道返回给我的消息的最小大小是 49 个字节,我首先读取这 49 个字节(阻塞直到我得到它们或 10s 过期)以填充stream_get_meta_data'sunread_bytes字段。一旦我有了这个,我会动态调整缓冲区大小,min(16*1024, unread_bytes)以便我一次读取 16k 或所有剩余字节,以较小者为准。在我的情况下,这通常只意味着两个通过循环,因为消息通常很小(49 字节 + 有效负载)。

系统现在挂起大约 3 秒而不是 10 秒,但它挂起等待最初的几个字节到达(而不是最后),这可以归结为网络延迟和其他正常因素。

4

2 回答 2

5

文档说“stream_get_contents() 对已经打开的流资源进行操作,并返回字符串中的剩余内容,最多为 maxlength 个字节并从指定的偏移量开始。”

因此,当您提供 5 作为 maxlength 时,它最多只能读取五个字节并继续。如果它不能读取最多 5 个字节,它将等待并在 10 秒内过期,您在 stream_set_timeout 中提到过

例子 :

//server side statement<br/>
$data = stream_get_contents($this->socket, 5);

//corresponding client code<br/>
fwrite($client, "1234");

在上述情况下,服务器将等到您再写一个字节 fwrite($client, "5");

于 2014-01-01T06:56:59.447 回答
1

我建议您只使用该sleep($seconds)功能,甚至该usleep($nanoseconds)功能。超时是为流本身设置的,而不是为每个stream_get_contents

于 2013-12-19T14:57:03.493 回答