QQ交流群:198941541
这一组合用于乒乓测试,其实concurrent_client + concurrent_server也是乒乓测试,只是测试方向不一样而已,如果你放开宏ASCS_WANT_MSG_SEND_NOTIFY,则这一组测试更像是echo_client + echo_server的测试,大家不用纠结名字。
既然是乒乓测试,那必须是收到消息之后再转发(或者发送下一个消息),从结果可以明显的看出来,乒乓测试的效率出奇的差,原因也一目了然,它完全没有用上ascs的收发缓存,也没有用上SOCKET的收发缓存。所以你得祈求你的业务不是乒乓测试这种类型,即一问一答再下一问,这样出来的吞吐量奇差,老板让你解释你上哪儿说理去。
乒乓测试很简单,我们就不再做过多解释了,只说一点,为了验证上面我说的吞吐量差的问题,我们可以在pingpong_client里面放开宏ASCS_WANT_MSG_SEND_NOTIFY (这样就不是严格的乒乓测试了,相当于发球方连发指定数量的球,每个都是新的,对方把每个球接回,发球方拿到回来的球就直接丢弃),这样就用上了SOCKET的发送缓存,你会看到,效率会明显提高,当然还是不如把ascs收发缓存一起用起来效率更高。那么怎么就用上了SOCKET的发送缓存呢?因为一但将消息写入SOCKET,on_msg_send就会被回调,我们在on_msg_send里面继续发送下一条消息,而不用等到对方的消息发送回来之后再发送下一条。
socket_management演示了socket对象的管理(客户端),它是服务端客户端同体的,另外还演示了关闭自动重连(宏ASCS_RECONNECT),我们当然要重点说说对象管理:
对象管理ascs就有,为什么还要自己实现一套呢?那是因为ascs不知道socket的key是什么,所以无法建立key与socket的映射,为此ascs实现一套id分配方案,采用64位整数(不循环使用)作为key来建立与socket的映射,这是最通用的一种方式,我也只能做成这样,但在实际应用中很有可能不是这样的,比如socket_management里面演示的,通过名称(字符串)与socket建立映射,由于客户端的连接是你自己主动创建的,你可以在创建的时候,把它加入到映射表里面:
auto socket_ptr = create_object();
assert(socket_ptr);
if (add_socket(socket_ptr))
link_map[name] = socket_ptr;
其中add_socket是multi_client_base的方法,意思是把连接添加给它,create_object是object_pool接口,意思是创建一个socket对象。当我们需要向指定的socket发送消息时:
auto iter = link_map.find(name);
return iter != std::end(link_map) ? iter->second->send_msg(...) : false;
那服务端如何做对象管理呢?服务端是异步accept连接,所以会先创建socket,再accept连接,这就会带来一个问题,当创建socket时,并不知道它代表哪个客户端,只有当客户端连上来并登陆成功之后,才知道它的名称,此时需要你在处理消息时,当确定是登陆消息之后,从消息里面解出名称再放到link_map里面。你可以建立名称与socket的对应关系,也可以建立名称与socket id的对应关系。如果采用前者,那么要记得当连接断开之后,要从link_map里面删除它,否则这条连接将得不到重用或者释放;如果采用后者,那么在查询socket的时候,要先查询link_map得到socket id,再调用object_pool的find函数找到对应的socket,需要多一次map查询,不过object_pool采用的hash表,访问的效率是足够高的,效率影响很小。