由于某种原因,zlib.deflate
过滤器似乎不适用于stream_socket_pair()
生成的套接字对。所有可以从第二个套接字读取的是双字节zlib头,之后的所有内容都是NULL。
示例:
<?php
list($in, $out) = stream_socket_pair(STREAM_PF_UNIX,
STREAM_SOCK_STREAM,
STREAM_IPPROTO_IP);
$params = array('level' => 6, 'window' => 15, 'memory' => 9);
stream_filter_append($in, 'zlib.deflate', STREAM_FILTER_WRITE, $params);
stream_set_blocking($in, 0);
stream_set_blocking($out, 0);
fwrite($in, 'Some big long string.');
$compressed = fread($out, 1024);
var_dump($compressed);
fwrite($in, 'Some big long string, take two.');
$compressed = fread($out, 1024);
var_dump($compressed);
fwrite($in, 'Some big long string - third time is the charm?');
$compressed = fread($out, 1024);
var_dump($compressed);
输出:
string(2) "x�"
string(0) ""
string(0) ""
如果我注释掉stream_filter_append()
的调用,则流写入/读取功能正常,数据全部被转储三次,如果我将zlib过滤后的流导入文件而不是通过套接字对,压缩数据写得正确。因此两个部分分别正确运行,但不能一起运行。这是我应该报告的PHP错误,还是我的错误?
此问题从解决方案分支到this related question。
答案 0 :(得分:3)
我曾参与PHP源代码并找到了解决方法。
要了解在
期间我跟踪代码的情况....
for ($i = 0 ; $i < 3 ; $i++) {
fwrite($s[0], ...);
fread($s[1], ...);
fflush($s[0], ...);
fread($s[1], ...);
}
循环,我发现deflate
函数永远不会被设置为Z_SYNC_FLUSH
标志,因为backets_in
旅中没有新数据。
我的修复是管理( PSFS_FLAG_FLUSH_INC
标志设置AND
没有对deflate函数执行迭代案例)扩展
if (flags & PSFS_FLAG_FLUSH_CLOSE) {
管理FLUSH_INC
:
if (flags & PSFS_FLAG_FLUSH_CLOSE || (flags & PSFS_FLAG_FLUSH_INC && to_be_flushed)) {
This downloadable patch适用于PHP的debian squeeze
版本,但文件的当前git版本更接近它,所以我想将端口修复只是(几行)。
如果出现一些副作用,请与我联系。
答案 1 :(得分:2)
翻阅the C source code,问题是过滤器总是让zlib's deflate()
function决定在生成压缩输出之前要累积多少数据。除非deflate()
输出一些数据(参见第235行)或设置PSFS_FLAG_FLUSH_CLOSE
标志位(第250行),否则deflate过滤器不会创建要传递的新数据存储桶。这就是为什么在关闭$in
之前只看到头字节的原因;第一次调用deflate()
会输出两个标头字节,因此data->strm.avail_out
为2,并为这两个字节创建一个新的存储桶来传递。
请注意fflush()
因zlib过滤器的已知问题而无效。请参阅:Bug #48725 Support for flushing in zlib stream。
不幸的是,似乎没有一个很好的解决方法。我开始通过扩展php_user_filter
在PHP中编写过滤器,但很快就遇到了php_user_filter
不暴露标志位的问题,只是flags & PSFS_FLAG_FLUSH_CLOSE
(filter()
的第四个参数方法,一个通常名为$closing
的布尔参数。您需要自己修改C源代码以修复Bug#48725。或者,重写它。
就我个人而言,我会考虑重新编写它,因为代码似乎有一些令人费解的问题:
status = deflate(&(data->strm), flags & PSFS_FLAG_FLUSH_CLOSE ? Z_FULL_FLUSH : (flags & PSFS_FLAG_FLUSH_INC ? Z_SYNC_FLUSH : Z_NO_FLUSH));
似乎很奇怪,因为写作时,我不知道为什么flags
不是PSFS_FLAG_NORMAL
。是否可以写&amp;同时冲洗?在任何情况下,处理标志都应该在while
循环之外通过“in”bucket旅进行,就像在此循环之外处理PSFS_FLAG_FLUSH_CLOSE
一样。第221行,memcpy
到data->strm.next_in
似乎忽略了data->strm.avail_in
可能不为零的事实,因此压缩输出可能会跳过某些写入数据。例如,请参阅zlib手册中的以下文本:
如果不能处理所有输入(因为输出缓冲区中没有足够的空间),
next_in
和avail_in
会更新,此时将继续处理,以便下一次调用{{ 1}}。
换句话说,deflate()
可能不为零。
avail_in
语句,if
可能应为if (data->strm.avail_out < data->outbuf_len)
或if (data->strm.avail_out)
。if (data->strm.avail_out > 2)
不是*bytes_consumed = consumed;
。 http://www.php.net/manual/en/function.stream-filter-register.php的示例流都使用*bytes_consumed += consumed;
来更新+=
。 编辑: $consumed
是正确的。 The standard filter implementations全部使用*bytes_consumed = consumed;
而不是=
来更新第五个参数指向的+=
值。此外,即使PHP端的size_t
在$consumed += ...
上有效转换为+=
(参见ext/standard/user_filters.c
的第206和231行),也会调用本机过滤器函数对于第五个参数,size_t
指针或指向NULL
的指针设置为0(参见main/streams/filter.c
的第361行和第452行)。
答案 2 :(得分:1)
您需要在写入之后关闭流以在数据从读取进入之前对其进行刷新。
list($in, $out) = stream_socket_pair(STREAM_PF_UNIX,
STREAM_SOCK_STREAM,
STREAM_IPPROTO_IP);
$params = array('level' => 6, 'window' => 15, 'memory' => 9);
stream_filter_append($out, 'zlib.deflate', STREAM_FILTER_WRITE, $params);
stream_set_blocking($out, 0);
stream_set_blocking($in, 0);
fwrite($out, 'Some big long string.');
fclose($out);
$compressed = fread($in, 1024);
echo "Compressed:" . bin2hex($compressed) . "<br>\n";
list($in, $out) = stream_socket_pair(STREAM_PF_UNIX,
STREAM_SOCK_STREAM,
STREAM_IPPROTO_IP);
$params = array('level' => 6, 'window' => 15, 'memory' => 9);
stream_filter_append($out, 'zlib.deflate', STREAM_FILTER_WRITE, $params);
stream_set_blocking($out, 0);
stream_set_blocking($in, 0);
fwrite($out, 'Some big long string, take two.');
fclose($out);
$compressed = fread($in, 1024);
echo "Compressed:" . bin2hex($compressed) . "<br>\n";
list($in, $out) = stream_socket_pair(STREAM_PF_UNIX,
STREAM_SOCK_STREAM,
STREAM_IPPROTO_IP);
$params = array('level' => 6, 'window' => 15, 'memory' => 9);
stream_filter_append($out, 'zlib.deflate', STREAM_FILTER_WRITE, $params);
stream_set_blocking($out, 0);
stream_set_blocking($in, 0);
fwrite($out, 'Some big long string - third time is the charm?');
fclose($out);
$compressed = fread($in, 1024);
echo "Compressed:" . bin2hex($compressed) . "<br>\n";
产生: 压缩:789c0bcecf4d5548ca4c57c8c9cf4b57282e29cacc4bd70300532b079c 压缩:789c0bcecf4d5548ca4c57c8c9cf4b57282e29cacc4bd7512849cc4e552829cfd70300b1b50b07 压缩:789c0bcecf4d5548ca4c57c8c9cf4b57282e29ca0452ba0a25199945290a259940c9cc62202f55213923b128d71e008e4c108c
此外,我将$ in和$切换,因为写入$让我很困惑。