系统架构
RakNet的代码可以分为三个部分:网络通信、使用网络通信的插件和通用游戏工具(功能)。
- 网络通信时由两个用户类来提供的,它们分别是RakPeerInterface和TCPInterface。游戏中主要使用到的类的是基于UDP协议的RakPeerInterface,它提供的功能主要包括:连接、连接管理、拥塞控制、远程服务器检测、带外消息(?)、连接数据、延迟和模拟丢包、禁止列表和安全连接等等。
- TCPInterface对TCP进行了包装,可以用于基于TCP协议的外部系统,如采用了TCP接口的用来报告远程服务器Crash信息的EmailSender类。大多数插件模块也支持TCPInterface。与自动补丁(autopatcher)一样,它也可以用来进行文件传输。RakNet中的插件是一种可以附加到RakPeer或者PakcetizedTCP实例中的类,这是用了设计模式中的责任链模式。若PakPeer或者PacketizedTCP的实例被附加上某个插件,当该实例的Receive()函数被调用时,附加上去的插件就会自动更新,并且对要进入网络流中的消息进行过滤或者拒绝操作。插件提供的功能包括在点对点环境中的主机发现、文件传输、网络地址转换、声音通信、远程过程调用和游戏对象的应答。
- 通用游戏工具包括主服务器(master server)、错误报告、通过Gmail pop Server上发送邮件和一个SQL登陆服务器。
RakPeer内部架构
RakPeer.h 文件提供了UDP通信的基本功能。建议大多数游戏都应该使用RakPeer而不是TCP。在启动时,RakPeer就启动了两个线程:一个线程用来等待即将来到的数据包,另一个线程用来执行周期性的更新,例如发现丢失的连接或者ping。用户指定最大的连接数量和一个RemoteSystem结构体的数组,大小与最大链接数量相同。每一个连接或者尝试连接都对应于一个RemoteSystem结构体,这个结构体中包含着一个在两个系统之间进行拥塞控制的类。每一个连接都是由SystemAddress或者RakNetGuid来标识的。RakNetGuid是一个随机产生的数字,每一个RakPeer的实例中RakNetGuid都是不同的。
连接是通过包含着连接请求的UDP消息建立起来的,这个消息中还有一个“离线消息”标识符。这个标识符用来区别到底是真正的离线消息呢,还是连接消息,只不过恰好和离线消息相匹配了。考虑到丢包因素,连接请求会在一个短的时间帧中重复发送。如果它支持“逐渐减少的MTU”,也会在路径MTU检测中使用。
当一个连接请求到达时,RakPeer发送内部的状态数据,例如RakNetGUID。检查该连接是否在禁止列表中,重复连接列表,进行一些安全措施。如果该连接被标记为安全的,那么安全连接协议将发送一些额外的数据。如果连接成功了,用户会收到这个通知,要么是ID_CONNECTION_REQUEST_ACCEPTED,要么是ID_NEW_INCOMING_CONNECTION。当然,如果失败了,也会以相似的方式来通知用户。
RakNet把用户连接服务器时发出的消息进行复制并缓存。如果这些消息加上头部已经超过了MTU,那么消息就会被自动分片。在一个周期性的间隔时间里,这些消息被聚合成一个数据报在一些限制下再次发出,这些限制包括拥塞控制的转发限制和MTU。如果某个数据报发出但是没有收到ACK,它们将被重发。消息发送不是可靠的,以一定的优先级来发送。ACK是不受拥塞控制管理的。重新发送的数据报的优先级总是大于新的数据包的优先级。
一开始,到来的数据报在一个阻塞式的recvfrom线程里。当一个数据报到达时,立即记录一个时间戳,这个数据报进入一个线程安全的队列中等待处理线程的调度。此时,处理线程将会收到一个信号,它要么立即处理这个消息,要么在下一个可用时间内处理这个消息。
到来的数据报以字节序列被检查,另外,源IP也要被检查。如果该消息被标记为未连接的,并且消息的发送者也没有连接到我们,那么仅仅对消息类型检测一小部分,例如可能是连接请求等。如果消息被标记为已连接的,并且消息的发送者也已经连接到了我么,那么该消息将被“可靠层”类所检测,例如拥塞控制和其他通信相关的信息(ACK,NAK,重发,加密,大量消息的重新组合)等。
连接消息首先被RakPeer处理。
其他所有的消息都被插件处理,或者返回给用户。调用RakPeer::Receive()会使得所有插件的更新函数调用一次,然后返回一个消息。从RakPeer::Receive()中返回的消息将返回给用户。最好是在循环中调用Receive()函数来得到所有的消息。
其他系统
NetWorkIDObject类提供了让系统访问通用对象的能力,它被对象成员的远程方法调用所使用。每一个对象都被赋予一个64位的随机数字,并且可以通过哈希表来查找对象的指针。
SystemAddress结构体用来表示一个远程系统,包含有远程系统的二进制IP和端口,支持IPv4和IPv6.
BitStream类由RakNet原生支持,它同时被用户类和内部类使用。它主要用于把单个bit写入流中和自动大小端转换,可以通过定义宏_BITSTREAM_NATIVE_END来定义。
RakNet UML Diagram