### Handler

网络事件处理器，处理各种网络连接事件。

==注意：beyod会为每个listener创建独立的handler对象，并不是为每个连接创建handler, 这是基于性能的考虑。==

自定义的handler类，应该从beyoioHandler继承，并覆盖父类事件回调方法，实现自己的业务逻辑。

==一般来说，最常用的回调是onMessage, onClose 即收到数据包、连接断开后的业务处理。==

beyod/Handler.php

    <?php

    namespace beyod;

    use yii\base\Event;
    use beyod\CloseEvent;
    use beyod\ErrorEvent;
    use beyod\MessageEvent;
    use yii\base\Behavior;

    class Handler extends Behavior
    {
        /**
         * @var Listenner 所属的Listener对象
         */
        public $owner;

        /**
        * 当前handler支持的事件回调方法，一般来说，无须覆盖此方法。
        */
        public function events()
        {
            return [
                Server::ON_CONNECT => 'onConnect',
                Server::ON_MESSAGE => 'onMessage',
                Server::ON_CLOSE => 'onClose',
                Server::ON_ERROR => 'onError',
                Server::ON_BAD_PACKET => 'onBadPacket',
                Server::ON_BUFFER_FULL => 'onBufferFull',
                Server::ON_BUFFER_DRAIN => 'onBufferDrain',
                Server::ON_SSL_HANDSHAKED => 'onSSLHandShaked',
                Server::ON_UDP_PACKET => 'onUdpPacket',
                Server::ON_START_ACCEPT => 'onStartAccept',
                Server::ON_STOP_ACCEPT => 'onStopAccept',
            ];
        }

        /**
         * handler对象被构造后调用的初始化方法，根据需要覆盖即可。
         * @param Event $event
         */
        public function init(){}

        /**
        * 当handler所属的listenner开始准备接收客户端连接时，此事件被激活。
        * $event->sender即指向当前Listennner
        */
        public function onStartAccept(Event $event){}

        /**
         * 同上，当前handler所属的listenner停止接收客户端连接时，此事件被激活。
         * @param Event $event
         */

        public function onStopAccept(Event $event){}

        /**
         * 当收到一个udp数据包时的回调
         * @param UdpMessageEvent $event
         $event->message即为当前数据包内容（具体格式与指定的Parser::decode方法有关）。
         $event->sender为当前listenner对象。
         */
        public function onUdpPacket(UdpMessageEvent $event){}

        /**
         * 当完整的tcp数据包解析成功后的事件回调。
         * ```php
         * $connection = $event->sender; //sender为当前connection对象
         * $message= $event->message;    //数据包内容
         * $connection->send('hello, your request '.$message);
         * ```
         * @param MessageEvent $event
         */
        public function onMessage(MessageEvent $event){}

        /**
         * 当一个tcp连接建立之后的事件回调, $event->sender为当前connection对象
         * ```php
         * $event->sender->send("hi, provide login: ");
         * ```
         * @param IOEvent $event
         */
        public function onConnect(IOEvent $event){}

        /**
         * 当前tcp连接断开之后的事件回调 $event->by可以判断连接是被哪一方主动断开的。
         * ```
         * $event->by === CloseEvent::BY_SELF; //closed by self
         * $event->by === CloseEvent::BY_PEER; //closed by remote
         * ```
         * @example 当前连接断开后，向其它客户端发送通知:
         * ```
         foreach($event->listenner->connections as $conn) {
            if($conn->id == $event->sender->id || $conn->isClosed()) continue;
            $conn->send($event->sender->id."");
         }
         *```

         * @param CloseEvent $event
         */
        public function onClose(CloseEvent $event){}

        /**
         * 输入输出过程中，出现错误，此事件被激活，一般来说，忽略即可(beyod会自己处理关闭连接)。
         * ```php
         * $event->code; //error code
         * $event->errstr; //error messag
         * $event->sender; //current connection
         * ```
         * @param ErrorEvent $event
         */
        public function onError(ErrorEvent $event){
            \Yii::error($event->sender." ".$event->errstr." ".$event->code, 'beyod');
        }

        /**
         * 当数据包无法解析（过大，无法解析，格式错误）时，此事件激活（此时是否关闭连接，取决于你）。
         * ```php
         * $event->code; //error code
         * $event->errstr; //error messag
         * $event->sender; //current connection
         * ```
         * @param ErrorEvent $event
         */

        public function onBadPacket(ErrorEvent $event){
            \Yii::error($event->sender.' '.$event->code.' '.$event->errstr, 'beyoio');
            $this->sendErrorResponse($event);
        }

        /**
         * 当前连接的发送缓冲区已满时的事件回调，此时应该暂时发送，以免造成数据包丢失。
         * 特别是发送大文件时，一定要注意缓冲区耗尽的问题，beyod自带的http server支持大文件（不限大小）下载，可参阅 beyoio\protocol\http\Handler::sendFile()
         * ```php
         * $event->code; //error code
         * $event->errstr; //error messag
         * $event->sender; //current connection
         * ```
         * @param IOEvent $event
         */
        public function onBufferFull(IOEvent $event){}

        /**
         * 当前连接的发送缓冲区已空时的事件回调。发送大数据包时，应该使用这种方式，防止缓冲区耗尽。
         * ```php
         * $event->code; //error code
         * $event->errstr; //error messag
         * $event->sender; //current connection
         * ```
         * @param IOEvent $event
         */
        public function onBufferDrain(IOEvent $event){

        }

        /**
         * 当一个TCP SSL连接握手成功后的回调。一般无须重写此方法。
         * @param IOEvent $event
         */
        public function onSSLHandshaked(IOEvent $event){}

        /**
         * 当收到一个错误的数据包时，可以做一些期待的操作（如向客户端发送错误信息），如果要发送错误消息并断开连接：

     $event->sender->close('invalid packet');
     ```
     * 是否断开连接， 由开发者自行决定，beyod并不会武断地断开连接（而只是清空当前的接收缓冲区）
     * @param ErrorEvent $event
     */
    public function sendErrorResponse(ErrorEvent $event)
    {

    }

}

    **connection**
    代表一个tcp连接，最常用的方法就是send, close

    一般我们在handler的回调方法完成网络事件处理。

    connection重要属性：

id: int 当前连接的id。beyod使用id重用机制，不会有id不断增长耗尽的问题。当某个连接断开后，它所占用的id就会被后续其它新的连接所使用。
connect_at: float 连接建立的时间戳。
request_at: float 最新一次完整数据包发送完成的时间戳，0表示还发送数据包。

    重要的方法：

send($message, $raw=false); 向客户端发送数据 $raw==true指明不使用parser的封包操作，而是直接发送。传送大体积消息的中间数据段时，往往就使用使用$raw==true。

close($message, $raw=false); 向客户端发送数据，然后断开连接

isClosed(): bool 判断连接是否已经断开，当群发消息时，必须判断没有断开才能发送。

setAttribute(string $name, mixed $value); 在连接对象上保存一些属性值

getAttribute(string $name, $default=null); 读取属性值

比如当客户端验证成功之后，我们可以使用上述方法设置其登录状态数据。

addFileHandler(string $name, resource $fp); 增加一个文件句柄
getFileHandler(string $name); 读取寄存的文件句柄
closeFileHandler(string $name); 关闭文件句柄

运行时，可能需要为当前连接打开一系列文件，但当业务复杂，连接异常断开时，这些文件资源需要及时关闭，否则导致系统资源耗尽，通过使用这种方式，相关的文件资源在连接释放时统一进行关闭，从而提升了进程的稳定性。