php中user_filter的使用
golang中io库提供了统一的流操作方法,php中存在类似功能吗?答案是有的,并且同golang相似地提供了统一的操作流(网络、
文件、压缩数据等数据的抽象)的方法。也就是说php的流函数提供了处理不同流资源的统一接口。
流数据各式各样,针对不同的流自然需要不同的协议,这就是流封装协议。
php中的流协议由schema://resource 组成,schema就是流封装协议,resource就是数据资源。
如
file_get_contentx('http://123.abc')
fopen('file://abc.txt', 'a')
fgets('php://stdin')
相比于完全实现自己的流封装协议(因为都替你实现好了,这里有个例子),工作中更可能用到的是在读取或写入的流的过程中对流进行写操作。这就可能使用到流上下文,作用是影响流的行为,同时不同的流封装协议有不同的上下文选项。如使用http流协议发送post请求
$postdata = http_build_query(
array(
'var1' => 'some content',
'var2' => 'doh'
)
);
$opts = array('http' =>
array(
'method' => 'POST',
'header' => 'Content-type: application/x-www-form-urlencoded',
'content' => $postdata
)
);
$context = stream_context_create($opts);
$result = file_get_contents('http://example.com/submit.php', false, $context);
同样的我们可以自定义的流过滤器影响读写流的行为,可实现修改、过滤流等功能。下面是用user_filter实现一个简单的拆包过程
# 客户端 php已经提供了类似功能,这里只是演示,没什么实际意义的例子
# the filter really doesn't consume the connenction's buffer in this example, so there may be repetitive content, we can totally solve the situation in next episode
class unpack_filter extends php_user_filter
{
public string $data = '';
public function filter($in, $out, &$consumed, $closing)
{
while ($bucket = stream_bucket_make_writeable($in)) {
$this->data .= $bucket->data;
$consumed += $bucket->datalen;
$p = explode("\n", $this->data);
// TODO 完善分包处理
$last = array_pop($p);
if ($last !== '') {
$this->data = $last;
}
array_walk($p, function ($pp) use ($in, $out, &$len) {
if (!empty($pp)) {
$nb = stream_bucket_new($this->stream, 'got filtered' . $pp . "test");
stream_bucket_append($out, $nb);
}
});
}
return PSFS_PASS_ON;
}
}
$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($sock, '127.0.0.1', 9503);
$res = socket_export_stream($sock);
stream_filter_register('unpack_filter', 'unpack_filter');
stream_filter_append($res, 'unpack_filter');
while (1) {
var_dump(stream_get_line($res, 0, "test"));
}
# 服务端 复制自swoole官网
$server = new Swoole\Server('127.0.0.1', 9503);
$server->on('start', function ($server) {
echo "TCP Server is started at tcp://127.0.0.1:9503\n";
});
$server->on('connect', function ($server, $fd) {
// $data = 'abcd1234';
// $data = pack('N', strlen($data)) . $data;
$data = "abcd1234\n";
for ($i = 0; $i < 10000; $i++) {
$server->send($fd, $data);
}
});
$server->on('receive', function ($server, $fd, $reactor_id, $data) {
$server->send($fd, "Swoole: {$data}");
});
$server->on('close', function ($server, $fd) {
echo "connection close: {$fd}\n";
});
$server->start();
参考内置filter