关于套接字:php stream_get_contents挂在流的末尾

php stream_get_contents hangs at the end of the stream

Solution at the end of the question

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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;
    }
}

输出如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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

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

另外,为了您的信息,我使用ETX标记(3)表示消息的结尾,因此如果我单击此消息,也将停止,而不仅仅是流的结尾。

难道我做错了什么?有更好的方法吗?

提前致谢...

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

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

解决方法(由于使用chathux):

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

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

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


文档说:" stream_get_contents()对已经打开的流资源进行操作,并以字符串形式返回剩余内容,直到最大长度的字节,并从指定的偏移量开始。"

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

例如:

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

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

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


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