php - 丢失 6 个字节,但前提是套接字安静 60 秒?

标签 php sockets curl chunked-encoding

我一直在并排运行两个套接字客户端,收集 http 流数据(不是 Twitter,而是类似的东西)。数据以分块编码传递。

其中一个客户端是 curl(在命令行上,而不是 php-curl),http 和 https 都可以正常工作。另一个是我自己的 PHP 脚本,使用 fsockopenfgets。适用于 https,但我对 http 有特定问题。有多具体?仅当流安静 60 秒时才会发生。如果只有 50 秒的安静,它就可以正常工作。我一直在将发送和接收的 curl 的 http header 与我的脚本进行比较,并消除了所有差异。我以为我知道关于 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 是 block 大小的十六进制。\r\n 标记 block 大小行的结尾。 block 从“{”开始。

在 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 或任何 block 大小。 但是,在那 60 秒的间隔之后,我在 $chunk_info 中得到的是:

datafeed":"demo.1d6.1m.ran...

所以,我似乎丢失了前六个字节:38 32 0d 0a 7b 22

所有后续 block 都很好,并且与 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 协议(protocol):tftp ftp telnet dict ldap ldaps http 文件 https ftps 特性:GSS-协商 IDN IPv6 大文件 NTLM SSL libz

带有 Suhosin-Patch (cli) 的 PHP 5.3.2-1ubuntu4.18(构建时间:2012 年 9 月 12 日 19:12:47)

服务器:Apache/2.2.14 (Ubuntu)

(到目前为止,我只测试过本地主机连接。)


循环的其余部分如下所示:

$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 秒后发生了什么变化,并学习如何阻止它发生。或许还能学到新的故障排除思路。将其视为血腥的求知欲:-)

最佳答案

这是错误修复:

$chunk_info=trim(fgets($fp)); //First line is hex digits giving us the length
if($chunk_info=='')continue; //Usually means the default 60 second time-out on fgets() was reached.
...

如果 fgets($fp) 返回东西,那么你有一个 block 要读取。如果那个东西是零,那么你有一个空白 block 要处理。但是当它返回nothing 时,这意味着 fgets 已经超时。 tcp://的默认超时时间似乎是 60 秒;而 ssl://的默认超时时间更长(抱歉,我还没有追踪到它是什么 - 它可能会永远阻塞)。

当没有可读取的数据 block 时尝试处理数据 block ,一切都变得不同步。因此被盗 6 个字节。

故障排除提示:

  1. 在代码中添加:echo "**".date("Y-m-d H:i:s");print_r(stream_get_meta_data($fp));ob_flush();flush();元数据有一个条目说明最后一个流操作何时超时。日期戳必不可少。
  2. 在命令行中使用 tcpdump -A -i lo port http 进行交叉引用。将时间戳与 PHP 调试行中的时间戳进行比较,使我能够发现可疑行为。

关于php - 丢失 6 个字节,但前提是套接字安静 60 秒?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13832646/

相关文章:

java - Apache Common Pool实现的TCP连接池中的非持久连接

c - 仅使用套接字实现单进程管道

php - CURL MULTI - 为什么有两个循环?

maven - Grizzly 服务器中止了 SSL 握手

php - 在循环中使用 sql 查询

php - 将错误保存到 MySQL 数据库

c++ - TCP - 如果客户端在服务器 accept() 之前调用 close() 会怎样

php - file_get_contents、curl、wget 失败并返回 403 响应

php - 替换给定 url 中的查询字符串值的更好方法

php - Jquery Chosen Select 插件在 PHP Ajax 响应中不起作用