NAT FULL CORE UDP 打洞

NAT 介绍

*NAT是将一个 私网IP 或者 内网IP 转化为 公网专属IP 提供全球使用,比如 游戏加速器、PS、XBOX Live 常用此类技术
此处有个坑,这个坑就是,NAPT与动态转化IP NAT不是一类,真正的NAT是转化IP,一般来说端口转化成公网端口都是NAPT(网络地址端口转换),NAPT目前最常用的技术,并不是 真NAT or APT,那个ISP(运营商)会那么好随便丢一个公网IP给你呢?
*

但是NAPT确确实实被列为 NAT 技术一种
*

NAT 类型

百度有说明: 传送,以下是本人简化说明,不严谨

Full Cone NAT 全锥形 NAT

  • NAT 1 (Full Cone)
  • Full Cone 可转化内网任意一个IP或者任意一个端口达到外网IP,我家设备就是走 NAT 1,由于光猫和我家路由器没有桥接,我电脑走NAT 3

Address-Restricted Cone NAT 受限锥型 NAT

  • NAT 2 (Address-Restricted Cone)
  • Address-Restricted Cone IP受限制,必须是 内网主机 发出连接一个 服务器IP和端口,并且将 内网IP 和 内网端口 映射到外网,服务器才能回应,不然服务器是无法连接到你 设备IP,就是过滤掉 内网设备 没有 连接到对应 IP服务器 发送的消息

Port-Restricted Cone NAT 端口受限锥型 NAT

  • NAT 3 (Port-Restricted Cone)
  • Port-Restricted Cone IP和端口都受限制,必须是 内网主机 发出对应 服务器IP 和 服务器端口,并且端口映射到一个外网端口,之后双方都可以通信,如果有一个 不与对应连接的IP 强制给你这个端口发送消息,将会被过滤掉。

Symmetric NAT 对称性 NAT (最糟糕的NAT)

  • NAT 4 (Symmetric NAT)
  • Port-Restricted Cone 最糟糕的类型,内网主机发出的任何一个请求,外网端口都会随机抽取幸运端口映射,由于是随机端口,无法进行连接

UDP 打洞

P2P 中心化 图归路

有些是有版权的,自己画一个又能怎么样

-解析 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
这属于abc连接方式,又类似ab类型,这里的abc和ab是我自己独创的思维,只是简化一些说法。
客户端A去连接服务器,然后客户端B也连接服务器
abc : A -> Server <- B
然后中心化服务器Server(b)已知双方打开的外网端口,并且记录成一个字典。
# 中心化服务器最好加入 Token 密钥验证,方便处理连接那个对应的IP
# 我这个代码加入了 Token(key) 来验证B客户端想连接那个IP
客户A 向服务器发起Key,中心化服务器看到Key之后,建立字典存入对应的IP和端口
客户B 向服务器发起验证Key,中心化服务器看到验证的Key之后,遍历字典寻找对应的IP和端口,反馈给客户B
客户B 拿到客户A对外的IP和端口,使用无面向连接的UDP发送数据给客户A
ab : B -> A
客户A 看到 某客户 发送的消息,进行回应之后可以通讯
ab : A -> B
abc之后就是ab,非常好理解

最好NAT环境基础为NAT 3 (端口受限锥型NAT)

Github 代码: (点我) p2p_udp_center

  • server.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import socket

# 创建 UDP
ftcp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
#0.0.0.0与127.0.0.1也不一致,我在Linux上跑的是0.0.0.0
ip = "0.0.0.0"
port = 2333
# bind ip and port
ftcp.bind((ip,port))
#存储的字典
lists = {}

while True:
data,conn = ftcp.recvfrom(1024)
print("连接IP : " ,conn)
datac = data[0]
datac = data.decode()
# 验证开头是否是^,如果是则为验证码
# 验证码长度至少为4
# print(datac[0])
if datac[0] == "^" and len(datac) > 4:
# 添加字典用于验证
lists.update({
datac:{
"ip":conn[0],
"port":conn[1]
}
})
# print(lists)
elif datac[0] != "^" and len(datac) > 4:
# 获取data[key]
# 缺点我这里没有写,如果是B客户发出请求,那么我直接抛出异常,因为字典没有对应Key
get_key = lists["^"+datac]

print("进行NAT的IP: {}:{} <= {}:{} ".format(get_key['ip'],str(get_key['port']),conn[0],conn[1]))
# 向 建立方 发送 连接方 数据
ftcp.sendto(str('{"ip":\"'+ conn[0] +'\","port":\"'+ str(conn[1]) +'\"}').encode(),(get_key['ip'],int(get_key['port'])))
# 向 连接方 发送 建立方 数据
ftcp.sendto(str('{"ip":\"'+ get_key['ip'] +'\","port":\"'+ str(get_key['port']) +'\"}').encode(),(conn[0],conn[1]))
  • client.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import socket,json

def B():
# 创建socket
B = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
#用于验证的验证码
Key = "HOWAREYOU"
#发送验证码给中心化服务器
B.sendto(Key.encode("GBK"),('47.100.234.158',2333))
#接收JSON
recv_data = B.recvfrom(1024)
print(recv_data[0])
#解析JSON
datas = json.loads(recv_data[0].decode())
while True:
#第一次建立必须回应对方
B.sendto("".encode(),(datas['ip'],int(datas['port'])))
#接受数据
recv_datas,addrs = B.recvfrom(1024)
print(recv_datas.decode())
if len(recv_datas.decode()) > 0:
B.sendto('Hello World'.encode(),(datas['ip'],int(datas['port'])))

def A():
# 创建socket
A = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
#验证码
Key = "^HOWAREYOU"
#发送验证码给中心化服务器存储
A.sendto(Key.encode("GBK"),('47.100.234.158',2333))
#等待JSON
recv_data = A.recvfrom(1024)
print(recv_data[0])
#解析JSON
datas = json.loads(recv_data[0].decode())
while True:
#第一次建立必须回应对方
A.sendto("".encode(),(datas['ip'],int(datas['port'])))
#接受数据
recv_datas,addrs = A.recvfrom(1024)
print(recv_datas.decode())
text = input("请输入内容: ")
if len(text) > 0:
A.sendto(text.encode(),(datas['ip'],int(datas['port'])))
if __name__ == "__main__":
print(" - - - - A客户 : A - B客户 : B - - - -")
bools = input("输入您的客户字母: ")
if bools == "A": A()
if bools == "B": B()

成功实例图



张知识

1
2
3
4
5
6
7
8
9
1.UDP 发送数据函数
sendto(data,(ip,port))

2.UDP 接收数据函数
recvfrom(size)

3.Sokcet UDP 孔
#非底层的UDP代码,Sokcet是一个API应用层
socket.socket(socket.AF_INET, socket.SOCK_DGRAM)