UDP打洞原理与N2N内网穿透
UDP打洞原理
通过UDP路由验证实现NAT穿越是一种在处于使用了NAT的私有网络中的Internet主机之间建立双向UDP连接的方法。由于NAT的行为是非标准化的,因此它并不能应用于所有类型的NAT。
其基本思想是这样的:让位于NAT后的两台主机都与处于公共地址空间的、众所周知的第三台服务器相连,然后,一旦NAT设备建立好UDP状态信息就转为直接通信,并寄希望于NAT设备会在分组其实是从另外一个主机传送过来的情况下仍然保持当前状态。
这项技术需要一个圆锥型NAT设备才能够正常工作。对称型NAT不能使用这项技术。
这项技术在P2P软件和VoIP电话领域被广泛采用。它是Skype用以绕过防火墙和NAT设备的技术之一。
相同的技术有时还被用于TCP连接——尽管远没有UDP成功。
原理
假设有两台分别处于各自的私有网络中的主机:A和B;NA和NB是两个网络的NAT设备,分别拥有IP地址P1和P2;S是一个使用了一个众所周知的、从全球任何地方都能访问得到的IP地址的公共服务器
步骤一:A和B分别和S建立UDP连接;NAT设备NA和NB创建UDP转换状态并分配临时的外部端口号
步骤二:S检查UDP包,看A和B的端口是否是正在被使用的(否则的话N1和N2应该是应用了端口随机分配,这会让路由验证变得更麻烦)
步骤三:如果端口不是随机化的,那么A和B各自选择端口X和Y,并告知S。S会让A发送UDP包到P2:Y,让B发送UDP包到P1:X
步骤四:A和B通过转换好的IP地址和端口直接联系到对方的NAT设备;
Server 端部分代码
//这里是对UDT的启动记性初始化操作
if (UDT::ERROR == UDT::startup())
{
cout<<"startup: "<<UDT::getlasterror().getErrorMessage()<<endl;
}else{
cout<<"startup suc..."<<endl;
}
//socket
//像声明一个普通的socket一样声明一个UDTSOCKET
UDTSOCKET serv = UDT::socket(AF_INET, SOCK_DGRAM, 0);
if (UDT::ERROR == serv)
{
cout<<"socket: "<<UDT::getlasterror().getErrorMessage()<<endl;
}else{
cout<<"client suc..."<<endl;
}
//声明udp socket,这里是udp的哈,不是udt
int sersocket = socket(AF_INET,SOCK_DGRAM,0);
if (SOCKET_ERROR == sersocket)
{
cout<<"udp socket error!"<<endl;
}else{
cout<<"clientsocket suc..."<<endl;
}
//为了能够在局域网中直接进行处理,先默认设置两个
sockaddr_in my_addr,client_addr;
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(atoi(argv[1]));
my_addr.sin_addr.s_addr = INADDR_ANY;
memset(&(my_addr.sin_zero), '\0', 8);
bind(sersocket,(struct sockaddr*)&my_addr,sizeof(my_addr));
client_addr.sin_family = AF_INET;
client_addr.sin_port = htons(atoi(argv[3]));
client_addr.sin_addr.s_addr = inet_addr(argv[2]);
//client_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
memset(&(client_addr.sin_zero), '\0', 8);
int mss = 1052;//最大传输单位
//设置收发缓冲区大小 接收限时 和地址重用
if( !( UDT::ERROR != (UDT::setsockopt(serv, 0, UDT_SNDBUF, new int(32000), sizeof(int)))
&& UDT::ERROR != (UDT::setsockopt(serv, 0, UDP_RCVBUF, new int(32000), sizeof(int)))
&& UDT::ERROR != (UDT::setsockopt(serv,0,UDT_REUSEADDR,new int(1),sizeof(int)))
&& UDT::ERROR != (UDT::setso