使用streamwrapper进行自定义解包封包
使用streamwrapper进行自定义解包封包
stream wrapper和filter的关系:stream wrapper能够使resource流化,比如http://www.baidu.com/index.php,php内置的http stream wrapper就会将后面的resource流化,让php能够以流的形式操作资源,而filter是用来操纵这些流(meta-stream)的。
基本使用如下:
# php://filter/read=[filter]/resource=[stream]
$input = fopen('php://filter/read=string.toupper/resource=php://stdin', 'r');
while (false !== ($line = fgets($input))) {
echo $line;
}
# 我们从$input读取的就是经过filter操作后的大写数据
示例如下:实现通过fgets每次读取一条完成消息的功能
# 客户端 wrapper
class UnpackWrapper
{
public const Name = 'myunpack';
private $resource;
// fopen('myunpack://127.0.0.1:9503', 'r')
// fgets() 返回一条完整的数据
public function stream_open($path, $mode, $options, &$opened_path)
{
if (!$this->resource) {
$addr = str_replace(self::Name, 'tcp', $path);
$this->resource = stream_socket_client($addr);
}
return true;
}
public function stream_read($count)
{
// TODO 待完善
// 此处只是示例,各位可自行完善,这种写法可能会导致缓冲区数据堆积,如果采用多路复用可能会造成IO事件的持续触发
// 需要在应用层实现buffer
$head = fread($this->resource, 4);
$len = unpack('N', $head)[1];
$data = fread($this->resource, $len);
return $data . "\n";
}
public function stream_close()
{
@fclose($this->resource);
}
public function stream_eof()
{
// TODO 待完善
return is_resource($this->resource) ? false : true;
}
}
stream_register_wrapper(UnpackWrapper::Name, UnpackWrapper::class);
var_dump(stream_get_wrappers());
$fp = fopen('myunpack://127.0.0.1:9503', 'r');
while (1) {
$data = fgets($fp);
var_dump($data);
sleep(1);
}
# 服务端代码复制于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 = 'abcd';
$data = pack('N', strlen($data)) . $data;
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();
以上只是展示了确实存在通过自定义streamwrapper进行拆解包的可能,感兴趣的可以自行完善,并且实现一个封包的wrapper。
我们下期再见