使用streamwrapper进行自定义解包封包

使用streamwrapper进行自定义解包封包

streamWrapper class

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。

我们下期再见

posted @ 2022-03-06 21:40  alwayslinger  阅读(124)  评论(0编辑  收藏  举报