# Parser 协议解析器

协议解析器，负责输入字节流的输入解析、数据解包、输出封包。

一旦客户端和服务器建立连接之后，就可以双向传输数据流，这时需要parser介入，进行数据流的解析和解包、封包操作，parser的主要工作：

1. 解析客户端发送的数据流，直到收到一个完整的数据包，这个过程称为输入(input)阶段。
2. 当收到完整的数据包时，对数据包字节流二次组装，转换成我们期待的特殊格式（如构造成Request对象），这个过程称之为解包(decode)阶段。
3. 当向客户端发送数据时，我们需要将应用层传递的响应内容转换为协议相关的字节流， 这个过程称为封包(encode)阶段。
4. 在parser运行过程中，如果出现数据包过大，或数据包格式错误，应该抛出异常，由connection处理这种异常情况。

   特别注意：beyod会为每个listener创建一个独立的parser对象，而不是为每个连接各自创建parser对象，这基于性能考虑。

为什么需要数据包解析？
针对tcp协议，输入字节是以水流形式源源不断到达服务器的，在服务器没有收到完整的数据包前，是无法处理客户端请求的。所以我们需要协议解析器，找到效数据包的边界。

udp数据包是有界的，即便同一个客户端发送过来的不同的包，它们之间也是独立的关系（甚至晚发的包先到达）。所以input方法是不必要的, 但是decode和encode却是有意义的。

所有Parser类都应该从beyod\Parser继承，实现自己的数据流处理逻辑。

beyod\Parser：

    <?php

    namespace beyod;

    use yii\base\Behavior;
    use yii\base\Object;

    use beyod\helpers\Formatter;

    class Parser extends Behavior
    {

        const ERROR_TOO_LARGE_PACKET = 1;
        const ERROR_BAD_PACKET = 2;

        /**
         * @var integer max packet size bytes limitation, 0 menas no limit.
         * 单个数据包的最大字节数，默认为8MB, 配置时可使用 16M/64K的形式, 如果客户端发送的单个数据包长度大于此值，将触发Handler::onBadPacket回调（此时是否关闭连接，由你确定）
    如果你实现了自定义的Parser，请勿忘记在input方法中判断数据包长度，以免产生恶意的攻击导致内存耗尽。
         */
        public $max_packet_size = 8388608;

        /**
         * parser对象初始化方法，被构造之后被运行
         */
        public function init(){
            $this->max_packet_size = Formatter::getBytes($this->max_packet_size)
        }

        /**
         * 默认的输入判断，只是简单判定了输入数据包是否超出限制，input的返回结果：
         * 0： 表示还未收到完整的数据，将继续接收并调用input，直到接收完整数据包。
         * 正数：指明了数据包的字节长度。
         * 抛出异常： 数据包错误，当前连接的输入缓冲区被清空，触发Handler::onBadPacket事件。
         * @param $buffer string
         * @param $connection Connection 当前的连接，udp是无连接的，不需要数据包边界判断，永远不会调用此方法。
         */
        public function input($buffer, $connection)
        {
            $len = strlen($buffer);

            if ($this->max_packet_size >0 && $len >= $this->max_packet_size) {
                throw new \Exception($connection.' request packet size exceed max_packet_size ', Server::ERROR_LARGE_PACKET);
            }

            return $len;
        }

         /**
         * 解包操作，当收到完整的数据包时，可以把字节流转换成我们期待的格式（如Response对象），以便于处理。
         * 后续将触发 Handler::onMessage()事件。
         * @param $buffer string
         * @param $connection Connection 当前的连接，udp是无连接的，此值为null
         */
        public function decode($buffer, $connection)
        {
            return $buffer;
        }

        /**
        * 当向客户端发送数据时，可以将原始的响应对象转成字节流并附加公共协议字段。
        * @param $buffer string
        * @param $connection Connection 当前的连接，udp是无连接的，此值为null
        */
        public function encode($buffer, $connection)
        {
            return $buffer;
        }
    }