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

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

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

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

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

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

==所有Parser类都应该从beyoioParser继承,实现自己的数据流处理逻辑。==

beyoioParser类的属性:

max_packet_size: int 单个数据包的最大字节数,默认为33554432(32MB), 或在配置时可使用 16M/64K的形式, 如果客户端发送的单个数据包长度大于此值,将触发Handler::onBadPacket回调(此时是否关闭连接,由你确定)  
如果你实现了自定义的Parser,请勿忘记在input方法中判断数据包限制,以免产生恶意的攻击导致内存耗尽。

beyodParser:

<?php

namespace beyoio;

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

use beyod\helpers\Formatter;

class Parser extends Behavior
{
    /**
     * 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;
    } 
}