使用带有套接字对的 zlib 过滤器 [英] Using zlib filter with a socket pair
问题描述
出于某种原因,zlib.deflate
过滤器似乎不适用于由 stream_socket_pair()
生成的套接字对.所有可以从第二个套接字读取的是双字节 zlib 标头,之后的所有内容都是 NULL.
For some reason, the zlib.deflate
filter doesn't seem to be working with socket pairs generated by stream_socket_pair()
. All that can be read from the second socket is the two-byte zlib header, and everything after that is 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 错误,还是我的错误?
If I comment out the call to stream_filter_append()
, the stream writing/reading functions correctly, with the data being dumped in its entirety all three times, and if I direct the zlib filtered stream into a file instead of through the socket pair, the compressed data is written correctly. So both parts function correctly separately, but not together. Is this a PHP bug that I should report, or an error on my part?
这个问题是从一个解决方案分支到 这个相关问题.
推荐答案
查看 C 源代码,问题是过滤器总是让 zlib 的 deflate()
函数决定在产生压缩输出之前要累积多少数据.除非 deflate()
输出一些数据(参见第 235 行)或设置了 PSFS_FLAG_FLUSH_CLOSE
标志位(第 250 行),否则 deflate 过滤器不会创建要传递的新数据桶.这就是为什么在关闭 $in
之前你只能看到头字节的原因;第一次调用 deflate()
输出两个标头字节,所以 data->strm.avail_out
是 2 并且为这两个字节创建一个新的桶来传递.
Looking through the C source code, the problem is that the filter always lets zlib's deflate()
function decide how much data to accumulate before producing compressed output. The deflate filter does not create a new data bucket to pass on unless deflate()
outputs some data (see line 235) or the PSFS_FLAG_FLUSH_CLOSE
flag bit is set (line 250). That's why you only see the header bytes until you close $in
; the first call to deflate()
outputs the two header bytes, so data->strm.avail_out
is 2 and a new bucket is created for these two bytes to pass on.
请注意 fflush()
由于 zlib 过滤器的已知问题而不起作用.请参阅:错误 #48725 支持在 zlib 流中刷新.
Note that fflush()
does not work because of a known issue with the zlib filter. See: Bug #48725 Support for flushing in zlib stream.
不幸的是,似乎没有一个很好的解决方法.我通过扩展 php_user_filter
开始在 PHP 中编写过滤器,但很快遇到了 php_user_filter
不公开标志位的问题,只有 flags &PSFS_FLAG_FLUSH_CLOSE
(filter()
方法的第四个参数,一个布尔参数,通常命名为 $closure
).您需要自己修改 C 源代码以修复错误 #48725.或者,重写它.
Unfortunately, there does not appear to be a nice work-around to this. I started writing a filter in PHP by extending php_user_filter
, but quickly ran into the problem that php_user_filter
does not expose the flag bits, only whether flags & PSFS_FLAG_FLUSH_CLOSE
(the fourth parameter to the filter()
method, a boolean argument commonly named $closing
). You would need to modify the C sources yourself to fix Bug #48725. Alternatively, re-write it.
就我个人而言,我会考虑重新编写它,因为代码似乎存在一些令人吃惊的问题:
Personally I would consider re-writing it because there seems to be a few eyebrow-raising issues with the code:
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
.是否可以编写 &同时冲洗?在任何情况下,处理标志都应该在while
循环之外通过in"bucket brigade 完成,就像在这个循环之外处理PSFS_FLAG_FLUSH_CLOSE
一样.第 221 行,
memcpy
到data->strm.next_in
似乎忽略了data->strm.avail_in
可能不为零,因此压缩输出可能会跳过一些写入数据.例如,请参阅 zlib 手册中的以下文本:
status = deflate(&(data->strm), flags & PSFS_FLAG_FLUSH_CLOSE ? Z_FULL_FLUSH : (flags & PSFS_FLAG_FLUSH_INC ? Z_SYNC_FLUSH : Z_NO_FLUSH));
seems odd because when writing, I don't know whyflags
would be anything other thanPSFS_FLAG_NORMAL
. Is it possible to write & flush at the same time? In any case, handling the flags should be done outside of thewhile
loop through the "in" bucket brigade, like howPSFS_FLAG_FLUSH_CLOSE
is handled outside of this loop.Line 221, the
memcpy
todata->strm.next_in
seems to ignore the fact thatdata->strm.avail_in
may be non-zero, so the compressed output might skip some data of a write. See, for example, the following text from the zlib manual:
如果不是所有的输入都可以处理(因为输出缓冲区没有足够的空间),next_in
和 avail_in
被更新,处理将在此时恢复deflate()
的下一次调用.
If not all input can be processed (because there is not enough room in the output buffer),
next_in
andavail_in
are updated and processing will resume at this point for the next call ofdeflate()
.
换句话说,avail_in
可能不为零.
In other words, it is possible that avail_in
is non-zero.
*bytes_consumed = 消耗;
是正确的.标准过滤器实现都使用=
而不是+=
来更新第五个参数指向的size_t
值.此外,即使 PHP 端的 $consumed += ...
有效地转换为 size_t
上的 +=
(参见第 206 和 231 行ext/standard/user_filters.c
),使用 NULL
指针或指向 size_t
集的指针调用本机过滤器函数第五个参数为 0(参见 main/streams/filter.c
).
*bytes_consumed = consumed;
is correct. The standard filter implementations all use =
rather than +=
to update the size_t
value pointed to by the fifth parameter. Also, even though $consumed += ...
on the PHP side effectively translates to +=
on the size_t
(see lines 206 and 231 of ext/standard/user_filters.c
), the native filter function is called with either a NULL
pointer or a pointer to a size_t
set to 0 for the fifth argument (see lines 361 and 452 of main/streams/filter.c
).
这篇关于使用带有套接字对的 zlib 过滤器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!