系统概览
系统结构
RakNet大致上说定义了3个库:网络通信哭,用于网络通信的插件模块,和支撑功能。
网络通信是用两个类来提供的。RakPeer和TCPInterface。RakPeer是游戏使用的主要的类,它基于UDP。它提供了连接,连接管理,拥塞控制,远程服务器检测,带外数据,连接统计,延迟,丢包仿真,阻止列表和安全连接功能。
TCPInterface是一个TCP的包装类,用于和基于TCP的外部系统通信。例如,EmailSender类,用于报告远程系统的本亏消息。有一些插件也支持它,建议用于文件传输,例如自动补丁升级系统。
RakNet中的插件模块是附加到RakPeer或PakcktizedTCP实例的类。基类更新或过滤或注入消息到网络流的时候,附加的插件自动更新。插件提供了自动功能,例如在端到端环境下的主机确定,文件传输,NAT跨越,语音通信,远程调用,游戏对象复制。
支持的功能包括崩溃报告,通过Gmail的pop服务器发送邮件,SQL日志服务器,和基于服务器列表的PHP。
RakPeer内部结构
RakPeer.h 提供了UDP通信的基本功能,期望大多数的应用程序使用RakPeer而不是TCP。开始时,RakPeer启动两个线程——一个用于等待到来的数据包,另外一个用于执行周期的更新,例如检测连接丢失,或pings。用户制定了最多连接数,以及远程系统结构体的数组内在地分配成了这么个大小。每一个连接或连接尝试都赋值了一个远程系统结构体,这个远程系统结构体包含了一个类来管理两个连接系统之间的连接控制。连接是由SystemAddress或RakNetGuid来标识,后者是随即生成的,每一个RakPeer的实例对应于一个唯一的GUID。
连接是通过包涵了连接请求数据和一个“离线消息”标识符的UDP消息来建立。“离线消息”标识符用于区分真正的离线消息和与离线消息相似的连接消息。连接请求在一个短的时间短内重复发送,以免数据包丢失,并且如果支持,可以使用一个递减的MTU用于MTU路径检测。
连接请求到来时,RakPeer传输内部状态数据,例如RakNetGUID,在禁止列表中检验这些连接,重连列表。和其他的安全措施。如果连接是安全的,安全连接协议启动,发送额外的数据。一旦成功,通知用户连接成功,使用ID_CONNECTION_REQUEST_ACCEPTED或ID_NEW_INCOMING_CONNECTION。失败条件也是以一个类似的方式返回。
从用户发出的到连接系统的消息在底层复制,并且进行内部缓存。如果外出信息加上头比MTU大,这个消息就需要在内部进行分片。在一段内部的发送数据整合为一个单包,按照拥塞控制和MTU大小的出散户限制发送。对没有收到ACK的数据包需要进行重发。出现丢失数据包序列号的情况下,要发送NAKs。消息是不可靠发送的,在一个用户定义的门限检测到时不能发送。消息按照优先级发送,Acks没有进行拥塞控制。然而重新发送的数据和新的数据发送中,重发要比新的发送有更高的优先级。
当开始时,发来的数据到达阻塞的接收线程。数据包到来时,时间戳立即记录,然后将数据推进一个处理线程管理的线程安全的队列。给处理线程发出信号,这样如果线程休眠了,他可以立即处理消息,或者在下一个可用的时间处理消息。
接收的数据包要经过字节序列检测,以表明是否发送者认为它是正确的。此外,源IP地址也会检测。如果消息标明是非连接的数据包,发送者没有连接到我们系统,这样消息会针对一个非常小的接收类型范围进行检测,例如连接请求,带外消息。如果一个消息表明了是已经连接的连接的消息,发送系统与我们的系统是连接的,那么它是通过ReliabilityLayer类进行处理的,用于拥塞控制和其他的通信相关的信息(ACKs,NAKs,
重发,大数据包的组装)。
连接消息首先由RakPeer来处理。当前仅仅用于周期pings,检测丢失连接用户不应该在给定的门限内发送数据。所有的其他消息有插件处理或返回给用户。调用RakPeer::Receive()一次运行所有的插件的更新函数,并且返回一个消息。返回给用户的消息是从RakPeer::Receive返回的,每一次调用一条消息。需要在一个循环中调用一个循环获取所有的消息,知道没有消息为止。
其他的系统
NetworkIDObject类为系统提供了访问共同对象的功能,用于对象成员远程函数调用。每一个对象有一个64位的随即数赋值,可以用于通过一个哈希查找指针。
SystemAddress结构体是RakNet用于代表远程系统的结构体。它是IP地址和使用的系统的端口的二进制编码,支持IPv4和IPv6。
BitStream类位于BitStream.h中,是RakNet直接支持的。即是一个用户类,也是一个内部类。它主要用于写一位数据到流中,以及自动的Endian交换,可以通过注释在RakNetDefines.h中的__BITSTREAM_NATIVE_END实现。
结构的图形展现参考UML Diagram
带宽消耗
Post3.6201,使用新的拥塞控制方法。
每个数据报:
1字节的位标记
4字节的时间戳,用于计算RTT进行拥塞控制
3字节用于序列号,用于查询数据报的ACKs
每一条消息
1字节用于位标记
2字节用于消息长度
if(RELIABLE, RELIABLE_SEQUENCED, RELIABLE_ORDERED)
A. 3字节用于序列号,用于防止返回到用户重复的消息
If(UNRELIABLE_SEQUENCED,RELIABLE_SEQUENCED,RELIABLE_ORDERED)
A 3字节用于序列号,用于在相同信道按序识别消息
B 1字节用于信道排序
If(message over MTU)
A 4字节用于分片序号,为提高性能不需要压缩
B 2字节用于表示这段数据是哪一片
C 4字节用于分片好的索引,为了提高性能不进行压缩
3.6201和更早的3.x系列
每一个数据报
1位用于位标记
8字节用于时间戳,用于拥塞控制中使用的RTT值计算
每一条消息
4字节用于数据长度
4字节用于序列号,用于防止返回给用户重复消息
4位用于位标记
If(UNRELIABLE_SEQUENCED,RELIABLE_SEQUENCED,RELIABLE_ORDERED)
A 4字节用于序列号,以有序识别消息
B 4位用于信道排序
if (message over MTU)
A 4字节用于分片数,但是压缩,平均使用1-2字节
B 4字节用于标识这个分片属于哪一个数据包
C 4字节用于分片内数字的索引,但是经过压缩,因此平均使用1-2字节
消息是从游戏上发送的数据。所有在RakNet之间发送的消息组成了一个数据报。因此如果你发送仅仅发送一条消息,那么消耗就是1数据报加一条消息。如果发送5条消息,那么就是1个数据报加上5条消息。如果你发送一条消息,但是是MTU的十倍大,那么需要发送10个数据报,每一个包涵一条消息(消息被分片)。