Tutorials

NAT Traversal

This is used for peers behind a router, as is the case for most users. There are two methods of external port mapping, and both are covered here. To see usage of “catch-all” port mapping, just reference the example in the API docs.

Note

This example should not be necessary in a few iterations of the library. It’s necessary now because detecting whether or not the peer is behind a router (and thus needs NAT traversal) is not implemented. Eventually, this part will happen automatically.

UPnP

Universal Plug and Play is the first method used when using the generic traversal.PortMapping object.

The following example tries to map the local port 7777 to external port 8888, increasing the mapped port up to 5 times on failures.

import sys
from cicada.traversal import upnp

LOCAL_PORT = 7777
ATTEMPTS = 5

mapper = upnp.UPnP()
mapper.create()

eport = 8888
for i in xrange(ATTEMPTS):
    if mapper.add_port_mapping(LOCAL_PORT, eport, protocol="udp"):
        break

    if i == ATTEMPTS - 1: continue
    print "Failed to map %d <--> %d, trying %d." % (
          LOCAL_PORT, eport, eport + 1)
    eport += 1

else:
    print "Failed to map %d after 5 attempts." % LOCAL_PORT
    sys.exit(1)

print "Succeeded in mapping %d <--> %d." % (LOCAL_PORT, eport)
mapper.delete_port_mapping(LOCAL_PORT, protocol="udp")
mapper.cleanup()    # removes *all* mappings

NAT-PMP

This method is often used on Apple routers and is the backup method tried after UPnP. Using this method is actually identical to UPnP, as the API is designed to be identical. The only difference is that you should use the traversal.NatPMP instance instead.

Simple 2-Node Echo Server

In this sample, we’ll create a 2-peer Cicada swarm and echo messages back and forth between the peers.

""" Establishes a *local* swarm, echoing a message between them.
"""
import sys, time
from cicada import swarmlib
from cicada.traversal import portmapper

local_address = portmapper.PortMapper.get_local_address()
first, second = swarmlib.SwarmPeer(), swarmlib.SwarmPeer()

first.bind(local_address, 5000)
second.bind(local_address, 5001)
second.connect(*first.listener)

first.send(second.listener, "hello!")
src, data, _ = second.recv()
assert data == "hello!"
assert src.chord_addr == first.listener

second.send(first.listener, data[::-1])
src, data, _ = first.recv()
assert data == "!olleh"
assert src.chord_addr == second.listener