问题末尾的解决方案
我正在编写一个PHP应用程序,该应用程序向服务器发送消息,然后使用stream_get_contents
读取响应。我在安卓应用程序中以相同的方式与同一服务器通信。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字节后返回的任何数据。
第二版:现在已经看到长达40秒的绞刑。它似乎没有固定在10秒,但奇怪的是,它似乎每次都是很好的整数。
解决方案(感谢chathux):
CCD_ 2将阻塞直到它接收到$bytes
字节或者超时到期。这意味着我的代码已经到了最后,并试图读取5个字节(不存在),因此等待了10秒才放弃。
正如我所知,返回给我的消息的最小大小是49个字节,我首先读取这49个字节(阻塞直到我得到它们或10秒到期),以便填充stream_get_meta_data
的unread_bytes
字段。一旦我有了这个,我就动态地将缓冲区大小调整为min(16*1024, unread_bytes)
,这样我要么一次读取16k,要么读取所有剩余的字节,以较小的为准。在我的情况下,这通常只意味着两个消息通过循环,因为消息通常很小(49字节+有效负载)。
系统现在挂起大约3秒,而不是10秒,但它挂起等待最初的几个字节到达(而不是最后),这可以归因于网络延迟和其他正常因素。
文档中说"stream_get_contents()对已经打开的流资源进行操作,并返回字符串中的剩余内容,最大长度为字节,从指定的偏移量开始。">
因此,当您提供5作为最大长度时,它将只读取最多5个字节并继续。如果它最多不能读取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");
我建议您只使用sleep($seconds)
函数,甚至使用usleep($nanoseconds)
函数。超时是为流本身设置的,而不是为每个stream_get_contents
设置的