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)
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]))
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)