ascs 简明开发教程(二十三):回调注册

31 篇文章 0 订阅
31 篇文章 1 订阅

QQ交流群:198941541

如果你不想通过继承重写函数的方式使用ascs库,可以通过回调注册机制来代替,回调包括两类,一类是事件回调(比如on_msg_handle),一类是行为(配置)自定义(比如is_ready)。关于回调注册,在echo_server和client里面有简单的演示,可以参考。下面详细说说使用方法及注意事项:

假如你之前的用法是class your_socket : publc an_ascs_socket {};,那么现在你需要先 #include <ascs/ext/callbacks.h>,然后在原来使用your_socket的地方,改为直接使用 ascs::ext::callbacks::g_socket/c_socket/s_socket<an_ascs_socket>(udp 使用g_socket,客户端 socket使用c_socket,服务端socket使用s_socket),然后调用其register_xxxx 方法注册回调函数(注意要尽早,如果在start_service之前socket就已经创建了,那应该在start_service之前调用,如果是动态创建的socket,可以在其构造函数里面调用,或者在server的on_accept里面调用,或者在object_pool的on_create里面调用。ascs::ext::callbacks下面也包括了server和object_pool对象,用于对回调注册的支持,所以你也可以通过register_on_accept和register_on_create来在第一时间配置socket),下面看个例子,摘自 demo client,作用是关闭断线重连:

//we only want close reconnecting mechanism on these sockets, so it cannot be done by defining macro ASCS_RECONNECT to false
class short_client : public multi_client_base<c_socket<socks4::client_socket>>
{
public:
	...
	bool send_msg(std::string&& msg, unsigned short port, const std::string& ip)
	{
		auto socket_ptr = add_socket(port, ip);
		if (!socket_ptr)
			return false;

		//register event callback from outside of the socket, it also can be done from inside of the socket, see echo_server for more details
		socket_ptr->register_on_connect([](socks4::client_socket* socket) {socket->set_reconnect(false);}, true); //close reconnection mechanism

		//without following setting, socks4::client_socket will be downgraded to normal client_socket
		//socket_ptr->set_target_addr(9527, "172.27.0.14"); //target server address, original server address becomes SOCK4 server address
		return socket_ptr->send_msg(std::move(msg));
	}

private:
	unsigned short port;
	std::string ip;
};

核心代码是c_socket<socks4::client_socket>和socket_ptr->register_on_connect,注意 register_xxxx的函数命名,xxxx就是原来你需要重写的函数名,回调函数的签名就是xxxx的签名加上一个 socket 指针在最前面(注意这个socket不是c_socket本身,而是c_socket的那个唯一的模板参数,即例子中的socks4::client_socket,假设你typedef它为raw_socket(也为了后面方便引用它),register_xxxx的参数除了上面说的回调函数(std::function)之外,还有一个bool类型参数表示是否在调用完回调函数之后,是否继续调用raw_socket的xxxx函数,至于这有什么用,下面会讲到。

现在来看看register_xxxx函数真面目:

template<typename CallBack> void register_xxxx(CallBack&& cb, bool pass_on)

现在说说参数pass_on和在不同场合下的应用,pass_on表示是否在调用完回调函数之后,继续再调用raw_socket::xxxx函数,如果你捕获事件仅仅是为了记录一条log,然后继续ascs的逻辑,那么显然你应该设置pass_on为true,比如:

socket_ptr->register_on_unpack_error([](client_socket* socket) {write_log(...);}, true);

当参数pass_on为true时,ascs::ext::callbacks::g_socket/c_socket/s_socket的行为会根据xxxx函数的返回值类型而不同,如果函数xxxx的返回值为void,这是最简单的情况,在调了回调函数之后,再继续调用raw_socket::xxxx 函数;如果函数xxxx的返回值为bool,则只有回调函数返回true之后(如果回调函数是dummy的,则当成返回true),才会继续调用raw_socket::xxxx函数,最终返回值为cb(...) && raw_socket::xxxx(...);如果函数xxxx的返回值为bool之外的值(比如 int,size_t 等),则不管回调函数返回什么,都会继续调用raw_socket::xxxx(...),且最终返回raw_socket::xxxx(...) 的返回值。下面举例说明各种常用的应用场景:

一:为socket 的释放或者重用加入自己的逻辑):

socket_ptr->register_obsoleted([](client_socket* socket) {return your_obsoleted_judgement();},  true);

二:完全失效一个事件(使用dummy回调函数):

socket_ptr->register_on_unpack_error(std::function<void(client_socket*)>(), false);

三:unregister一个事件回调(使用 dummy回调函数):

socket_ptr->register_on_unpack_error(std::function<void(client_socket*)>(), true);

四:先调用raw_socket::xxxx(...) 再执行自定义回调(需要 xxxx 函数拥是public的):

socket_ptr->register_reset([](client_socket* socket) {socket->reset(); your_reset();}, false);

五:先调用 raw_socket::xxxx(...) 再执行自定义回调(xxxx 函数是protected的):

class your_socket : public c_socket<client_socket>

{
public:
    your_socket(...) : c_socket<client_socket>(...)
    {
        this->register_on_heartbeat_error([this](client_socket* socket) {
            client_socket::on_heartbeat_error();
            write_log();
        }, false);
    }
};

六:demo client演示了关闭断线重连(代码在文章开头,都是已有功能,只是用回调注册重新实现),其实这种方法稍微有点问题,因为add_socket之后,已经调用过 socket_ptr->start() 了 (在multi_socket_service::add_socket里面,multi_client_base::add_socket会调用它),这就是我在前面说,没有做到尽量早地调用socket_ptr->register_on_connect(...),于是demo client里面还演示了另外一种方法(注释状态),即用callbacks::object_pool 包装 object_pool,然后注册 on_create 事件并在里面调用object_ptr->set_reconnect(false),如下:

class short_client : public multi_client_base<socks4::client_socket, callbacks::object_pool<object_pool<socks4::client_socket>>>
{
...
};

short_client client2(...);
client2.register_on_create([](object_pool<socks4::client_socket>*, object_pool<socks4::client_socket>::object_ctype& object_ptr) {
    object_ptr->set_reconnect(false); //close reconnection mechanism
});

这其实就是演示了如何使用callbacks::object_pool并注册事件回调,它只有一个事件回调,即 on_create,其签名就是原来的on_create加一个原始的object_pool(callbacks::object_pool那个模板参数)指针在最前面。由于ascs::tcp::server_base 也是从object_pool继承的,所以这种用法同样适用于server(server上的socket对象是server自动创建的,multi_client_base上的socket是你通过add_socket创建的)。

七:demo echo_server演示了只接受一个客户端的服务端(都是已有功能,只是用回调注册重新实现),为此我们用callbacks::server来包装原来的server,并注册其async_accept_num和 on_accept事件,其签名是原来的xxxx函数签名加一个原始的server(callbacks::server那个模板参数)指针在最前面,如下:

single_service_pump<callbacks::server<server_base<normal_socket>>> normal_server_;
normal_server_.register_async_accept_num([](server_base<normal_socket>*) {return 1;});
normal_server_.register_on_accept([](server_base<normal_socket>* server, server_base<normal_socket>::object_ctype&) {server->stop_listen(); return true;}, false);

八:所有的回调函数里面,都不得调用callbacks::g_socket/c_socet/s_socket/object_pool/server 的同名函数(一般来说也是调用不了的,但有几个方法例外,因为需要从外部访问),如果必须要调用,只能调用原始对象类型(即callbacks::g_socket/c_socet/s_socket/object_pool/server 的模板参数类型,它们都有且仅有一个模板参数)的同名函数,否则会发生循环调用而耗尽调用栈内存。

上一篇 ascs 简明开发教程(22) 下一篇 ascs 简明开发教程(24)​​​​​​​

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值