我一直在并排运行两个套接字客户端,收集 http 流数据(不是 Twitter,而是类似的东西)。数据以分块编码的形式出现。
其中一个客户端是 curl(在命令行上,而不是 php-curl),其中 http 和 https 都可以正常工作。另一个是我自己的 PHP 脚本,使用fsockopen
和fgets
. 适用于 https,但我对 http 有一个特定的问题。具体到什么程度?仅当流安静 60 秒时才会发生这种情况。如果只有 50 秒的安静时间,它就可以正常工作。我一直在比较 curl 发送和接收到我的脚本的 http 标头,并删除了所有差异。我以为我知道所有关于 PHP 套接字的知识,尤其是分块编码,但现在是吃不起眼的馅饼的时候了,因为这个让我难过。
因此,使用“--trace - --trace-time”运行 curl,我看到在 60 秒安静期后的第一个数据包中出现了这种情况:
05:56:57.025023 <= Recv data, 136 bytes (0x88)
0000: 38 32 0d 0a 7b 22 64 61 74 61 66 65 65 64 22 3a 82..{"datafeed":
0010: 22 64 65 6d 6f 2e 31 64 36 2e 31 6d 2e 72 61 6e "demo.1d6.1m.ran
...
0080: 34 22 7d 5d 7d 0a 0d 0a 4"}]}...
82 是块大小的十六进制。\r\n 标记块大小行的结束。块从“{”开始。
在 PHP 端,我的循环是这样开始的:
while(true){
if(feof($fp)){fclose($fp);return "Remote server has closed\n";}
$chunk_info=trim(fgets($fp)); //First line is hex digits giving us the length
$len=hexdec($chunk_info); //$len includes the \r\n at the end of the chunk (despite what wikipedia says)
使用 https,或者间隔小于 60 秒,这可以正常工作,$len 为 100 或任何块大小。但是,在那 60 秒的间隔之后,我在 $chunk_info 中得到的是:
datafeed":"demo.1d6.1m.ran...
所以,我似乎丢失了前六个字节:38 32 0d 0a 7b 22
所有后续块都很好,并且与 curl 接收到的完全相同。
版本详情
curl 7.19.7 (x86_64-pc-linux-gnu) libcurl/7.19.7 OpenSSL/0.9.8k zlib/1.2.3.3 libidn/1.15 协议:tftp ftp telnet dict ldap ldaps http 文件 https ftps 功能:GSS-Negotiate IDN IPv6大文件 NTLM SSL 库
带有 Suhosin-Patch (cli) 的 PHP 5.3.2-1ubuntu4.18(构建时间:2012 年 9 月 12 日 19:12:47)
服务器:Apache/2.2.14 (Ubuntu)
(到目前为止,我只测试了 localhost 连接。)
循环的其余部分如下所示:
$s='';
$len+=2; //For the \r\n at the end of the chunk
while(!feof($fp)){
$s.=fread($fp,$len-strlen($s));
if(strlen($s)>=$len)break; //TODO: Can never be >$len, only ==$len??
}
$s=substr($s,0,-2);
if(!$s)continue;
$d=json_decode($s);
//Do something with $d here
}
(顺便说一句:在我到目前为止测试的方式中,代码在 60 秒的安静期之前只通过了一次这个循环。)
注意:我有很多变通方法可以让事情正常工作:例如强制使用 https,或使用 curl-php。这个问题是因为我想知道发生了什么,知道 60 秒后发生了什么变化,并学习如何阻止它发生。也许学习一个新的故障排除思路。把它想象成血腥的求知欲:-)