peer-to-peer简称P2P,即点对点技术。又称对等互联网络技术,是一种网络新技术,简单的说,P2P直接将计算机直接联系起来,在两台都是公网ip下的计算机可以轻松实现两台电脑的连接,但是运营商现在基本给的都是非公网ip,而且进入家庭中也会由路由器再次进行NAT,这样两台电脑将无法直接连接,如果传输信息,通常都是两台电脑向具有公网ip的服务器发送信息,再由服务器转发给对方,这样传输速度势必会大打折扣,采用p2p技术可将两台在NAT后的电脑直接连接,达到点对点传输。
p2p打洞的原理(采用udp):
两台在NAT之后的电脑无法直接建立连接,因此可以让两台电脑向向一个有公网ip的服务器发送请求,服务器会记录两个发送方的源地址,并将对方的源地址分别发送给发送方,这样两个发送方都拥有了对方的的地址,然后发送方可直接向对方的源地址发送信息,这样达到了点对点连接的效果。笔记记录了最基础的实现原理,目前只在NAT1,NAT2上可以测试成功,NAT3,NAT4理论上好像不行就没在测试。。。连个互相连接的源ip地址都是发送方最外层网关的ip,对于怎么从最外部网关连接到内部电脑的传输原理,在这里就不赘述了,就是记录下实现过程,有不对的地方望批评指正。
采用网络调试助手实现(3个不同的网络中):
1、建立一个公共服务器,主要作用为服务器在接收到2个请求之后分别将对方源地址发送给发送方。
2、第一台电脑网络调试助手,向服务器发送任意信息。
如上图 两台电脑分别向ip后三位位为246的服务器发送请求,服务器将对方的源地址返回,之后将调试助手远程主机ip修改为刚才接收到的地址,这样udp打洞就实现了,两台电脑可以愉快的与对方直接连接,发送信息,达到点对点传输的目的。采用网络调试助手进行打洞的连接有时候不成功,需要多试几次,且受到对方地址后要快速发送,不然就通不了了,这个应该和运营商设定的资源释放时间有关。
用python实现的代码如下:
server
import logging
import socket
logger = logging.getLogger()
addresses = []
def addr_to_msg(addr):
return '{}:{}'.format(addr[0], str(addr[1])).encode('utf-8')
def main(host='0.0.0.0', port=9999):
sock = socket.socket(socket.AF_INET, # Internet
socket.SOCK_DGRAM) # UDP
sock.bind((host, port))
while True:
data, addr = sock.recvfrom(1024) # buffer size is 1024 bytes
logger.info("connection from: %s", addr)
addresses.append(addr)
if len(addresses) >= 2:
logger.info("server - send client info to: %s", addresses[0])
sock.sendto(addr_to_msg(addresses[1]), addresses[0])
logger.info("server - send client info to: %s", addresses[1])
sock.sendto(addr_to_msg(addresses[0]), addresses[1])
addresses.pop(1)
addresses.pop(0)
if __name__ == '__main__':
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s')
main()
client
import logging
import socket
import threading
logger = logging.getLogger()
def msg_to_addr(data):
ip, port = data.decode('utf-8').strip().split(':')
return (ip, int(port))
def send_to(sock,addr):
while 1:
a=input(f"\r向{addr}发送:").encode()
sock.sendto(a, addr)
def to_recv(sock):
while 1:
data, addr1 = sock.recvfrom(1024)
print('\r 接收信息: 地址:{} 内容:{}'.format(addr1, data))
def main(host='39.104.133.246', port=9999):
sock = socket.socket(socket.AF_INET, # Internet
socket.SOCK_DGRAM) # UDP
sock.sendto(b'0', (host, port))
print(f"向服务器{host}发送")
while True:
print("开始等待接收1")
data, addr = sock.recvfrom(1024)
print('1、接收: 地址:{} 内容:{}'.format(addr, data))
addr = msg_to_addr(data)
print(f"2、获取新地址{addr}地址并开始发送")
sock.sendto(b'0', addr)
break
t2 = threading.Thread(target=to_recv, args=(sock,))
t2.start()
t1 = threading.Thread(target=send_to, args=(sock, addr,))
t1.start()
if __name__ == '__main__':
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s')
main()