网络事件处理器,处理各种网络连接事件。
==注意: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); 关闭文件句柄
运行时,可能需要为当前连接打开一系列文件,但当业务复杂,连接异常断开时,这些文件资源需要及时关闭,否则导致系统资源耗尽,通过使用这种方式,相关的文件资源在连接释放时统一进行关闭,从而提升了进程的稳定性。