ascs 简明开发教程(二十二):可靠的UDP传输

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

QQ交流群:198941541

可靠UDP基于KCP(GitHub - skywind3000/kcp: KCP - A Fast and Reliable ARQ Protocol),用户需要熟悉KCP的窗口大小,重传模式,流控等,ascs还是原来的原则,不封装KCP,用户通过ikcpcb* get_kcpcb() 得到ikcpcb做任意想要的配置,当然应该在数据收发之前(构造函数里面和reset虚函数里面,后者会在socket被重用之后马上被调用)。

可靠UDP必须是基于连接的UDP,ascs的udp socket在引入可靠UDP之前就已经支持UDP连接了,这也是引入可靠UDP必须要做的先决条件。然而KCP并不要求UDP套接字是连接的(连接的UDP套接字可以看成是套接字的一个属性),ascs要求它是连接的,主要原因有:

一:要求连接性,是要表达一层意思,那就是可靠UDP是成队的(跟TCP一样),而不能再像原始UDP一样,一个套接字可以给任意地址发送数据,也可以从任意地址接收数据,虽然它的确仍然可以,但那样KCP就不工作了;

二:对于连接的UDP套接字,发送消息时,不再需要指定对方地址,因为KCP的消息发送函数和消息发送回调函数均不带对方地址。

reliable_socket_base继承自原来的 UDP socket_base,所以如果你不创建KCP(create_kcpcb),可靠UDP会退化成普通的UDP,参考udp_test。

使用可靠UDP需要用户提供一个回调函数,签名为:int output(const char* buf, int len, ikcpcb * kcp, void* user),不支持仿函数(因为KCP是纯C的),由于ascs是header only的,我很难去提供这么一个函数而不造成重复定义,如果定义成inline或者static,那么编译得到的所有.o文件里面都将有一份实现,即使不造成重复定义也不好,如果大家有更好的办法,欢迎群里面讨论。用户在这个回调函数里面直接调用reliable_socket_base的output函数即可,无需考虑多线程安全的问题,参考udp_test。这个回调函数通过KCP函数ikcp_setoutput送给ikcpcb(如果没有这一步,编译无法检测但程序会core dump),ikcpcb通过reliable_socket_base::get_kcpcb() 得到,当然这之前你必须已经调用过reliable_socket_base::create_kcpcb 创建了ikcpcb。

可靠UDP,需要用宏ASCS_RELIABLE_UDP_NSND_QUE或者set_max_nsnd_que设置一个发送队列大小(消息条数)上限,这是因为KCP的缓存大小是无限的,所以我们必需要在必要时限速,当KCP的发送队列消息数量大于这个值之后,ascs将暂停向其输送(ikcp_send)新的消息,此时消息会停在ascs的发送缓存里面,而ascs的发送缓存也是有限制,这样最终将造成用户调用send_msg失败。

KCP也没有解决的问题——连接重置:

在决定用KCP之前,我也想过自己开发一个可靠UDP,但就是这个连接重置难倒了我,后来在群里朋友的推荐下,采用了KCP。不管谁来实现可靠UDP,总是会用缓存来解决消息丢失,消息重复,消息乱序等问题,所以缓存是很复杂的,那如果通信双方一方重启了,接收方可能会永远接收不到某个消息,那么这个消息之后的消息,即便是收到了,也不能派发出来(否则消息乱序),那么对于这种无法恢复的问题(或者设定时间内无法恢复的问题),我们需要重置连接,意思是通信双方从这一刻开始重新来过,过往的所有错误既往不咎,可惜这种自动连接重置很难实现,只能在业务层上来做。我推荐的做法是,再建一条TCP连接,专门用于通知这种连接重置,一方出问题恢复之后,通过TCP告诉对方,并协商一个新的conv(可以与之前的一样,也可以不一样,总之通信双方用相同的conv即可,conv用于创建ikcpcb,参考reliable_socket_base::create_kcpcb),然后双方开始重新创建新的ikcpcb,这样可靠UDP又可以工作了。

注意你不能随时调用reliable_socket_base::create_kcpcb,必须先关闭socket(force_shutdown等),对于single_reliable_socket_service,如果你想重用它,等到after_close被调用之后,你就可以调用reliable_socket_base::create_kcpcb重新创建ikcpcb了,然后调用start(或者service_pump带i_service的start_service)再次启用它,其实最简单的办法还是创建一个新的single_reliable_socket_service;对于multi_reliable_socket_service,你应该先调用object_pool的create_object得到一个socket(可能是个新的socket,也有可能重用一个老的socket,如果是重用,object_pool会先调用socket的reset,再返回给你),然后调用其create_kcpcb以重新创建ikcpcb,最后调用add_socket(ascs::multi_socket_service实现的那个版本);如果你非要通过ascs::udp::multi_socket_service_base::add_socket一步到位,那就只能在构造函数(新创建socket)或者reset(重用socket)里面调用create_kcpcb重新创建ikcpcb了。

在socket的on_close里面,会释放ikcpcb,所以被关闭了的socket,如果要重用,一定要重新创建ikcpcb。

可靠UDP的使用可以参考udp_test。

上一篇 ascs 简明开发教程(21) 下一篇 ascs 简明开发教程(23)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值