适当减少手机使用,有助于视力保护... 新的一年祝您:身体健康,阖家欢乐!~~

P2P中的UDP穿透方法以及PYTHON实现

浏览量:561 作者:admin 类别:: 学习笔记 更新时间:2022-02-13 12:23:59

本文摘自:https://www.cnblogs.com/gaokaitai/p/13306518.html



现在大部分的个人主机都是通过路由器连接外网,整个内网使用一个公共IP,由路由器进行内网IP、端口和外网IP、端口的映射(NAT)。

而这种映射方式只能由内网主动向外发送请求时才会建立,并通过映射出的该端口监听返回消息。

根据映射方式的不同,建立映射的规则也不同,允许接收消息的范围也不同。如(参考https://www.cnblogs.com/dyufei/p/7466924.html

  1、对称 NAT(Symmetric NAT) 尽量会保持内网端口和映射端口相同。但是同一端口发出的指向不同外网服务器端口的请求会映射不同的端口。

  2、锥形NAT(Cone NAT)同一内网主机/端口的消息都会映射到相同的端口

    2.1 完全锥形映射(Full Cone NAT ):映射的端口允许接收任意公网IP的消息

    2.2 限制锥形( Restricted Cone NAT):映射的端口只允许接收建立映射时对应的公网IP发送的消息

    2.3 端口限制锥形 NAT ( Port Restricted Cone NAT ):在2.2的基础上增加了接收端口限制

在锥形NAT的情况下进行udp打洞是相对容易实现的,具体思路如下:

  1、需要一台作为中继的公网服务器S

  2、假设有内网主机A、B,首先A主动向S发出请求,此时NAT建立映射(ip1,port1)->(ipa,porta)。然后B向S发出请求,建立(ip2,port2)->(ipb,portb)。

  3、此时服务器S拥有A、B的公网映射信息,并将A的信息发给B,B的信息发给A。

  4、如果是完全锥形映射,此时AB可以直接发送消息通信。但是另外两种并不能行。

  5、此时A主动向B发送消息,A的NAT会建立A->B的映射,映射端口同样是porta。该消息会被B的NAT阻拦,并不会被接收,但此时A已经向B打开了通道。

  6、此时再由B向A发送消息,打开B->A 的通道,由此A、B已经可以正常进行通讯



简单demo如下

Sever.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
import socket
import _thread
import json
import time
udpSocket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
udpSocket.bind(("***.***.***.***", 40002))#注意如果使用云服务器,此处应绑定云服务器内网ip
add=[]#保存所用连接用户
severPort=30001
def acc(sc):
    cont,destinf=sc.recvfrom(1024)
    print(cont.decode("utf-8"))
    print(destinf)
    jsn=json.loads(cont.decode("utf-8"))
    if(jsn["cod"]==1):#用户连接
        if(len(add)>0):
            for x in add:
                print(x)
                jtt={"cod":2,"msg":destinf}
                sc.sendto(json.dumps(jtt).encode("utf-8"),x)#向所有用户广播新加入用户的地址
            jtn={"cod":3,"msg":add}
            sc.sendto(json.dumps(jtn).encode('utf-8'),destinf)
        add.append(destinf)
    acc(sc)
_thread.start_new_thread(acc,(udpSocket,))
  
s=input("")
while(1==1):
    time.sleep(10)    

  

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
import socket
import _thread
import json
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(('www.baidu.com', 0))
ip = s.getsockname()[0]#获取本机局域网IP
udpSocket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
udpSocket.bind((ip, 30001))#绑定端口
otherAddr=[]#保存其他用户的地址信息
severAddr="***.***.***.***"
severPort=40002
def acc(sc):
    global otherAddr
    cont,destinf=sc.recvfrom(1024)
    jsn=json.loads(cont.decode("utf-8"))
    if(jsn["cod"]==2):#0打洞1初始连接2服务器向其他用户广播新加入地址3向新加入用户通知已在线人员4用户消息
        print(str(jsn['msg'])+"进入聊天")
        otherAddr.append(jsn['msg'])#把新加入用户添加到otherAddr
        sc.sendto('{"cod":0}'.encode('utf-8'),tuple(jsn['msg']))#打洞
    if(jsn["cod"]==3):
        otherAddr=jsn['msg']
        print("进入聊天,其他人有"+str(jsn['msg']))
        for x in jsn['msg']:
            sc.sendto('{"cod":0}'.encode('utf-8'),tuple(x))#打洞
    if(jsn["cod"]==4):
        print(str(destinf)+":"+jsn['msg'])
    acc(sc)
_thread.start_new_thread(acc,(udpSocket,))
jsn={"cod":1,"msg":"a"}
udpSocket.sendto(json.dumps(jsn).encode("utf-8"),(severAddr,severPort))#向服务器第一次请求
 
while(1==1):
    s=input("")
    if(s!=""):
        jsn={"cod":4,"msg":s}
        for x in otherAddr:#向所有其他用户广播
            udpSocket.sendto(json.dumps(jsn).encode("utf-8"),(x[0],x[1]))


关于博主
北京南城网络及弱电【张工】为您提供:
校园网、楼宇、村镇、园区,光纤网络覆盖,无线覆盖 华为|H3c|锐捷|爱快|panabit等品牌,路由、交换机、AC、AP 无源光网络(PON)设备,布署、调试、维运,等服务...
应急/临时光纤熔接...[藤仓22S]
博文为学习python所写:内容摘自网络、学习笔记、记事备忘。
QQ:872876353,欢迎交流学习、加Q注明来意。

诗文鉴赏

《满江红》· 岳飞
落星侵晓没,
残月半山低。
怒发冲冠,
凭栏处、潇潇雨歇。
抬望眼、仰天长啸,壮怀激烈。
三十功名尘与土,八千里路云和月。
莫等闲、白了少年头,
空悲切。
源于生活-记录日常
The site based on python 3 with flask...