NAT穿透,UDP打洞程式
阿新 • • 發佈:2019-01-24
在看NAT穿透和UDP打洞原理,網上都是講原理,沒有程式,我把程式寫出來。
server.py,輔助打洞的伺服器。
peer.server.py,被打洞的節點。
peer.client.py,主動打洞的節點。
基本原理是:
1. peer.client向peer.server發個包,把自己的洞打好,這樣peer.server可以給peer.client發包。
這個包會被NAT拒絕掉,所以peer.server是收不到這個包的(當然如果沒有NAT,譬如在一臺機器上,是可以收到的,就不算打洞了)。
2. peer.client向server發個包,server給peer.server發個包,peer.server再給peer.client發個包。
這樣peer.client就可以給peer.server發包了。因為peer.client在第1步把自己的洞打好了,所以兩者可以互發訊息。
3. 打洞完畢,peer.client可以和peer.server通訊。
用的三臺虛擬機器,server為bridge網絡卡,peer.server和peer.client為NAT網絡卡。
#!/usr/bin/python2.6 # server.py import signal; import sys; def handler(signum, frame): print 'usr press ctrl+c, exit'; sys.exit(0) signal.signal(signal.SIGINT, handler) ################################################################## import socket; import json; import time; if len(sys.argv) <= 1: print """Usage: %s <port> port the [UDP] port to bind. For example: %s 2013"""%(sys.argv[0], sys.argv[0]); sys.exit(1); port=sys.argv[1]; print "NAT traversal & udp hold-punching" print "Server-side which used to help the client behind NAT to hold-punching." max_packet_size = 4096 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM); s.bind(('', int(port))); print "UDP bind at %s"%(port); peers = []; def get_peer(code, required_peer_id): for peer in peers: id = peer["id"]; if id != required_peer_id: continue; (peer_id, peer_address) = (id, peer["address"]); return (code, peer_id, peer_address); return (1, 0, None); while True: (data, address) = s.recvfrom(max_packet_size); #print "get data from %s: %s"%(address, data); obj = json.loads(data); action = obj["action"]; code = 0; if action == "join": id = obj["id"]; for i in range(0, len(peers)): if peers[i]["id"] != id: continue; del peers[i]; break; peers.append({"id":id, "address":address}); print "[join] %s %s"%(id, address); continue; if action == "find": (code, peer_server_id, peer_server_address) = get_peer(code, obj["peer_server_id"]); (code, peer_client_id, peer_client_address) = get_peer(code, obj["peer_client_id"]); print "[find] %s %s find %s %s"%(peer_client_id, peer_server_id, peer_server_id, peer_server_address); s.sendto(json.dumps({"code": code, "peer_server_address": peer_server_address, "peer_server_id": peer_server_id, "peer_client_address":peer_client_address}), address); if action == "hole_punching": (code, peer_server_id, peer_server_address) = get_peer(code, obj["peer_server_id"]); (code, peer_client_id, peer_client_address) = get_peer(code, obj["peer_client_id"]); time.sleep(3); print "[hole-punching] (%s)%s <==> (%s)%s"%(peer_client_id, peer_client_address, peer_server_id, peer_server_address); s.sendto(json.dumps({"action": "hole_punching", "peer_server_address":peer_server_address, "peer_client_address": peer_client_address}), tuple(peer_server_address)); pass; s.close();
#!/usr/bin/python2.6 # peer.server.py import signal; import sys; def handler(signum, frame): print 'usr press ctrl+c, exit'; sys.exit(0) signal.signal(signal.SIGINT, handler) ################################################################## import socket; import json; import time; if len(sys.argv) <= 3: print """Usage: %s <server> <port> <id> server the server to connect to. port the UDP port to connect to. id the id of peer. For example: %s 192.168.20.118 2013 peer.server"""%(sys.argv[0], sys.argv[0]); sys.exit(1); (server, port, id) = sys.argv[1:]; port = int(port); print "NAT traversal & udp hold-punching" print "Peer-server-side which is behind NAT to hold-punching." print "peer-server means it wait for peer-client to hole-punching with" server_endpoint = (server, port) max_packet_size = 4096 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM); # join server s.sendto(json.dumps({"action": "join", "id": id}), server_endpoint); print "[join] %s"%(id) while True: # recv request from server. (data, address) = s.recvfrom(max_packet_size); #print "get data from %s: %s"%(address, data); obj = json.loads(data); action = obj["action"]; if action != "hole_punching": continue; if "peer_client_address" not in obj: continue; peer_client_address = obj["peer_client_address"]; peer_server_address = obj["peer_server_address"] print "[open_hole] by %s"%(str(address)); # send a packet to peer.client to open the hole. s.sendto(json.dumps({"action": "open_hole", "id": id}), tuple(peer_client_address)); data = json.dumps({"action": "video", "video": "xxxxx video xxxxxx"}) while True: ret = s.sendto(data, tuple(peer_client_address)); print "[success] %s ===> %s: %s"%(peer_server_address, peer_client_address, data); time.sleep(3); break; s.close();
#!/usr/bin/python2.6
# peer.client.py
import signal;
import sys;
def handler(signum, frame):
print 'usr press ctrl+c, exit';
sys.exit(0)
signal.signal(signal.SIGINT, handler)
##################################################################
import socket;
import json;
import time;
if len(sys.argv) <= 4:
print """Usage: %s <server> <port> <id> <peer_server_id>
server the server to connect to.
port the UDP port to connect to.
id the id of peer.
peer_server_id the id of peer to hole-punching to.
For example:
%s 192.168.20.118 2013 peer.client peer.server"""%(sys.argv[0], sys.argv[0]);
sys.exit(1);
(server, port, id, peer_server_id)=sys.argv[1:];
port = int(port);
print "NAT traversal & udp hold-punching"
print "Peer-side which is behind NAT to hold-punching."
server_endpoint = (server, port)
max_packet_size = 4096
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM);
# join server
s.sendto(json.dumps({"action": "join", "id": id}), server_endpoint);
print "[join] %s"%(id)
(peer_client_address, peer_server_address) = (None, None);
while True:
# find the peer to hole-punching
s.sendto(json.dumps({"action": "find", "peer_client_id": id, "peer_server_id": peer_server_id}), server_endpoint);
# discovery result
(data, address) = s.recvfrom(max_packet_size);
#print "get data from %s: %s"%(address, data);
obj = json.loads(data);
code = obj["code"];
if code is not 0:
print "[find] peer %s not found"%(peer_server_id);
time.sleep(1);
continue;
peer_server_address = obj["peer_server_address"];
peer_client_address = obj["peer_client_address"];
break;
# to punching hole.
print "[find] peer(%s) address is %s"%(peer_server_id, peer_server_address);
# step 1: directly send a packet to peer, open self tunnel.
print "[hole-punching] try to punching hole to %s"%(peer_server_address);
s.sendto(json.dumps({"action": "hole_punching", "peer_client_id": id, "peer_client_address":peer_client_address, "peer_server_address":peer_server_address}), tuple(peer_server_address));
# step 2: send a packet to server, open the peer tunnel.
print "[hole-punching] try to use server %s to punching hole"%(str(server_endpoint));
s.sendto(json.dumps({"action": "hole_punching", "peer_client_id": id, "peer_server_id": peer_server_id}), server_endpoint);
while True:
(data, address) = s.recvfrom(max_packet_size);
print "[success] %s ===> %s: %s"%(address, peer_client_address, data);
s.close();
執行結果如下: