🪐 可靠的UDP协议
可靠的UDP协议
这一章节中,我们会介绍引擎中支持的可靠的UDP协议(RUDP)!
前言:
我们知道,KBE引擎是支持TCP协议的,并且在许多网络应用中,为了可靠性都是采用TCP协议。那为什么我们引擎还要去支持RUDP协议呢?我们先来了解几个概念。
TCP
TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。
其中最核心的就是其可靠性,主要表现在:
- 数据包顺序保障
- 超时重传
- 丢包重发
- 流量控制
具体请参见WIKI百科:《Transmission Control Protocol》一文。
既然TCP协议有这么多优势,为什么还需要RUDP协议以及RUDP协议是什么呢?
什么是可靠的UDP协议(RUDP)?
我们先来看看传统的UDP协议是什么?
UDP
UDP(User Datagram Protocol)用户数据报协议,它与TCP协议一样用于处理数据包,是一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务。可靠性由上层应用实现,所以要实现UDP的可靠性传输,必须通过应用层来实现和控制。而我们引擎支持的RUDP协议就是基于UDP同时实现了可靠性的一种协议。UDP的详细介绍请参见WIKI百科:《User Datagram Protocol》一文。
引擎支持的RUDP协议
RUDP(Reliable UDP)是可靠的用户数据报协议。它旨在提供一种解决方案,比UDP可靠,比TCP开销小。其中UDP为了低时延、低带宽占用等特性,无法保证是否丢包、顺序是否一致,而TCP增加了太多复杂度和开销来达到可靠性。为了RUDP获得更高的服务质量,它扩展了UDP,并实现了类似于TCP的功能,且开销更小。
应用场景的选择
说了这么多,那对于KBE引擎开发者,在TCP和RUDP之间如何进行选择呢?
选择使用哪种类型的套接字协议,完全取决于开发者编写的应用类型。如MMO游戏,那么对于状态同步的准确性、顺序等有一定要求,一般采用TCP或RUDP。再比如类似直播音视频的应用,业务本身的数据是可以允许缺失的,同时其成本和时延要求比较高,那采用UDP或RUDP是个不错的选择。
总结如下:
应用类型 | RUDP | TCP |
---|---|---|
实时性要求敏感(如FPS) | 优选 | X |
带宽占用敏感(成本考虑) | X | 优选 |
提示
带宽占用上,RUDP比TCP大? 由于RUDP为了可靠性的保障以及根据应用需要而配置的高实时参数的情况下,流量会耗费更多。
如何在引擎应用中使用RUDP协议
在KBEngine中已经内置了RUDP协议的支持,为了使开发者对协议的选择更加自由简单,对相应的配置进行了简化并在客户端SDK上提供了配置入口。接下来我们来看看具体如何在引擎中使用RUDP协议。
1、客户端上使用RUDP进行连接
只需在合适的应用场景下,从客户端上进行选择和配置即可。
引擎所有支持的客户端SDK插件上,都对RUDP做了开关设置,且默认的都是开启使用的状态。同时,针对RUDP的特性,添加了BUFFER_MAX的设置。
一旦RUDP是打开状态,则在与LoginApp完成账户认证后,客户端会使用RUDP的方式与BaseApp进行连接,完成RUDP的通讯。
下面我们按照不同客户端进行阐述:
1.1、Unity3D
我们知道,Unity3D中,SDK插件的入口是clientapp脚本。如下图:

从图中红色框可以看到:
1、 Force Disable UDP
:是否强制关闭UDP功能,默认是不勾选状态,即开启RUDP。
2、 UDP_SEND_BUFFER_MAX
:设置UDP发送的缓冲区大小。
3、 UDP_RECV_BUFFER_MAX
:设置UDP接收的缓冲区大小。
开发者可视自己的应用场景的不同而对这三个参数进行不同的调整,就可完美、自由的控制RUDP协议!非常容易吧!
2、引擎端上可选的配置选项
服务端为了更好的控制RUDP协议,也相应增加了一些配置选项。
提示
项目的引擎端配置请在{项目资产库}/res/server/kbengine.xml下进行修改,如该文件中不存在的字段,请自行根据格式添加。
2.1、reliableUDP字段
在channelCommon字段中新增了reliableUDP字段,该字段中对RUDP进行了各个方面的配置。如下:
<reliableUDP>
<!-- Equivalent to TCP RCV_BUF, unit is the number of UDP-packages -->
<readPacketsQueueSize>
<internal> 1024 </internal>
<external> 128 </external>
</readPacketsQueueSize>
<!-- Equivalent to TCP SND_BU, unit is the number of UDP-packages -->
<writePacketsQueueSize>
<internal> 1024 </internal>
<external> 128 </external>
</writePacketsQueueSize>
<!-- internal update timer interval in millisec, 0 is default(100millisec) -->
<tickInterval> 10 </tickInterval>
<!-- Retransmission TimeOut(millisec) -->
<minRTO> 10 </minRTO>
<!-- ACK skipping times, 0 is default -->
<missAcksResend> 2 </missAcksResend>
<!-- change MTU size, 0 is default(1400) -->
<mtu> 0 </mtu>
<!-- false: disable congestion control -->
<congestionControl> false </congestionControl>
<nodelay> true </nodelay>
</reliableUDP>
其中:
readPacketsQueueSize
: int, 接收的缓冲区队列大小,单位是包的数量。该参数类似于TCP协议中的接收缓冲区大小。其中internal针对内部的RUDP协议,external则针对外部的提供给客户端的RUDP协议。
writePacketsQueueSize
: int,类似的,发送的缓冲区队列大小。
tickInterval
: int,内部的更新间隔,单位毫秒。默认值为0,表示100毫秒。
minRTO
: int,最小的重传超时时间,单位毫秒。当超过该时间时,认为需要重传。
missAcksResend
: int,当前跳过的或丢失的ACK数量超过该值时,认为需要重新发送。默认为0,表示不重新发送。
mtu
: int,最大传输单元,指所能通过的最大数据包大小,单位为字节。默认值为0,表示1400字节。
congestionControl
: bool,是否开启拥塞控制。false表示不开启拥塞控制。
nodelay
: bool,是否开启延迟。true表示不开启延迟。该参数类似于TCP协议中的NODELAY设置,具体请参考TCP-Forcing data delivery
2.2、externalUdpPorts
针对有外部访问需求的App,如BaseApp、LoginApp,在原有的TCP端口设置上,都增加了UDP端口设置字段。如下方的BaseApp配置块:
<baseapp>
<!-- 脚本入口模块, 相当于main函数
(Entry module, like the main-function)
-->
<entryScriptFile> kbemain </entryScriptFile>
<!-- 指定接口地址,可配置网卡名、MAC、IP
(Interface address specified, configurable NIC/MAC/IP)
-->
<internalInterface> </internalInterface>
<externalInterface> </externalInterface> <!-- Type: String -->
<!-- 强制指定外部IP地址或者域名,在某些网络环境下,可能会使用端口映射的方式来访问局域网内部的KBE服务器,那么KBE在当前
的机器上获得的外部地址是局域网地址,此时某些功能将会不正常。例如:账号激活邮件中给出的回调地址, 登录baseapp。
注意:服务端并不会检查这个地址的可用性,因为无法检查。
(Forced to specify an external IP-address or Domain-name, In some server environment, May use the port mapping to access KBE,
So KBE on current machines on the external IP address may be a LAN IP address, Then some functions will not normal.
For example: account activation email address given callback.
Note: the availability of server does not check the address, because cannot check)
-->
<externalAddress> </externalAddress> <!-- Type: String -->
<!-- 暴露给客户端的端口范围
(Exposed to the client port range)
-->
<externalTcpPorts_min> 20015 </externalTcpPorts_min> <!-- Type: Integer -->
<externalTcpPorts_max> 20019 </externalTcpPorts_max> <!-- Type: Integer -->
<externalUdpPorts_min> 20005 </externalUdpPorts_min> <!-- Type: Integer -->
<externalUdpPorts_max> 20009 </externalUdpPorts_max> <!-- Type: Integer -->
</baseapp>
熟悉引擎的开发者应该知道,externalTcpPorts_min和externalTcpPorts_max的意义,UDP也类似。
externalUdpPorts_min
: int,暴露给客户端的UDP端口范围的最小值。
externalUdpPorts_max
: int,暴露给客户端的UDP端口范围的最大值。0表示无限制。