使用带有套接字对的 zlib 过滤器 [英] Using zlib filter with a socket pair

查看:30
本文介绍了使用带有套接字对的 zlib 过滤器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

出于某种原因,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 行,memcpydata->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 why flags would be anything other than PSFS_FLAG_NORMAL. Is it possible to write & flush at the same time? In any case, handling the flags should be done outside of the while loop through the "in" bucket brigade, like how PSFS_FLAG_FLUSH_CLOSE is handled outside of this loop.
  • Line 221, the memcpy to data->strm.next_in seems to ignore the fact that data->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_inavail_in 被更新,处理将在此时恢复deflate() 的下一次调用.

If not all input can be processed (because there is not enough room in the output buffer), next_in and avail_in are updated and processing will resume at this point for the next call of deflate().

换句话说,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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆