协议解析器,负责输入字节流的输入解析、数据解包、输出封包。
一旦客户端和服务器建立连接之后,就可以双向传输数据流,这时需要parser介入,进行数据流的解析和解包、封包操作,parser的主要工作:
特别注意: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;
}
}