NAT穿透,UDP打洞程序

在看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();

执行结果如下:



  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

winlinvip

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值