NAT类型检测
要完成NAT穿透需要提前确定NAT类型
NAT穿透的成功几率依赖于NAT使用的算法类型。
Full cone NAT:可以从先前使用过的端口上接收到任何数据报。可以从远端的Peer接收到第一个数据报。
Address-Restricted cone NAT:只要数据报源IP地址是先前我们发送过数据的系统,那么可以从端口上收到数据。如果两个系统同时发送数据报,可以接收到第一个数据报。否则,在我们发送一个数据报以后才会收到第一个数据报。
Port-Restricted cone NAT:与Address-restricted cone NAT类似,但是我们需要发送到正确的远端IP和正确的远端端口。到不同目的地的相同的源地址和端口使用相同的映射。
Symmetric NAT: 为每一远端目的地选择同的端口。到不同目的地的相同的源地址和端口使用不同的映射。因为端口号不同,第一次的外部穿透尝试就会失败。如果要使得这种模式工作,它要求有端口预测(MAX_PREICTIVE_PORT_RANGE > 1),以及路由按序选择端口。
Success Graph
Router Type |
Full cone NAT |
Address-Restricted cone NAT |
Port-Restricted cone NAT |
Symmetric NAT |
Full cone NAT |
YES |
YES |
YES |
YES |
Address-Restricted cone NAT |
YES |
YES |
YES |
YES |
Port-Restricted cone NAT |
YES |
YES |
YES |
NO |
Symmetric NAT |
YES |
YES |
NO |
NO |
NatTypeDetection插件允许你检测你自己的NAT的类型,以及NAT穿透是否可以完成。这个要在加入游戏之前确定。
NAT 类型检测算法
1. 客户端在相同的IP地址打开两个端口。在NatTypeDetectionClient中,RakNet的socket是第一个端口,c2是第二个端口的socket,这两个socket在NatTypeDetection::DetectNATType()中创建。
2. 服务器在同一个IP地址上打开两个端口,以及在三个其他的IP地址上打开一个端口。这个在NatTypeDetectionServer::Startup()函数中完成。第一个IP地址上的第一个端口是正常的RakNet端口。第一个IP地址上的第二个端口是s1p2。其他的三个地址绑定到s2p3, s3p4和s4p5。
3. 客户端连接到服务器通常是在第一个Ip地址上实现。
4. 客户端请求NAT类型检测开始。
5. 服务器尝试向客户端的第二个端口发送数据。这个端口是前面没有打开过的端口,因此如果接收到了,那么客户端就没有位于NAT之后。这个动作可以再NatTypeDetectionServer::Update()方法中实现,通过STATE_TESTING_NONE_1和STATE_TESTING_NONE_2来定义。两次尝试的原因是每一次尝试出现两次。每一次尝试的时间是ping * 3 + 50毫秒。S4P5用在这里。
6. 服务器从不同的IP地址向客户端的不同端口发送数据,这个端口RakNet已经连接。如果接收到了数据,那么客户端可以从已经使用的端口上接收到任何源IP地址的数据报。这个情况通常是full-cone NAT。s2p3用于这个目的。
7. 在已经连接的Ip地址上从第二个端口发送数据,s102。如果接收到了数据,那么客户端的NAT类型是address-restriced cone NAT。
8. 客户端向服务器的另外一个IP地址发送数据,从第一个端口(已经连接)。如果IP地址和端口是相同的,那么客户端使用external IP地址和端口来处理来自相同源地址的所有连接。这个是port-restriced NAT类型。
9. 其他的都是symmetric NAT
客户端实现:
1. 创建一个插件实例:NatTypeDetectionServer nayTypeDetectionClient。
2. 将插件附加到RakPeerIntance实例上:rakPeer->AttachPlugin( &nayTypeDetectionClient);
3. 连接服务器,等待ID_CONNECTION_REQUEST_ACCEPTED消息。使用如下的代码来使用RakNet提供的免费服务器:rakPeer->(“8.17.250.34”, 60481, 0, 0);
4. 使用服务器的SystemAddress调用DetectNATType。
5. 等待ID_NAT_TYPE_DETECTION_RESULT消息
6. 第一个字节包含了你的NAT类型。参考NATTypeDetectionCommon.h的NATTypeDetectionResult枚举类型。
7. 为这个枚举类型提供了各种功能函数:CanConnect(), NATTypeDetectionResultToString(), NATTypeDetectionResultToStringFriendly()。
服务器实现:
1. 在某地设置一台主机,不要使用NAT/或者位于防火墙之后。(RakNet提供了一个免费的服务器,地址为8.17.250.34:60481,但是为了使用方便,你可能想要维护你自己的主机,以便长时间运行)。服务器必须有足够多的外部IP地址,正如在NAT Type Detection Algorithm中所描述的那样。
2. 创建一个插件实例:NatTypeDetectionServer natTypeDetectionServer。
3. 将插件附加到RakPeerInterface实例上:rakPeer->AttachPlugin( &natTypeDetectionServer);
4. 获得系统上的IP地址列表:
char ipList[ MAXIMUM_NUMBER_OF_INTERNAL_IDS ][ 16 ];
unsigned int binaryAddresses[MAXIMUM_NUMBER_OF_INTERNAL_IDS];
SocketLayer::Instance()->GetMyIP( ipList, binaryAddresses );
5. 调用natTypeDetectionServer.Startup( ip2, ip3, ip4);
ip2, ip3, ip4必须是没有使用的IP地址。如果你调用RakPeer::Startup()方法中将RakNet绑定到ip1,然后使用2nd到4th到ipList中。
例子:
参考\Samples\NATCompleteClient中的例子。
By 北洋小郭
转载请注明出处,请勿用于商业用途,谢谢!