Using P2PD from Python
Before we get started all Python examples assume:
The ‘selector’ event loop is being used.
The ‘spawn’ method is used as the multiprocessing start method.
You are familar with how to run asynchronous code.
This keeps the code consistent across platforms. The package sets these by default so if your application is using a different configuration it may not work properly with P2PD.
from p2pd import *
# Put your custom protocol code here.
async def msg_cb(msg, client_tup, pipe):
# E.G. add a ping feature to your protocol.
if b"PING" in msg:
await pipe.send(b"PONG")
# Warning: startup is slow - be patient.
async def example():
# Initalize p2pd.
netifaces = await init_p2pd()
#
# Start our main node server.
# The node implements your protocol.
node = await start_p2p_node(
netifaces=netifaces,
#
# Set to true for port forwarding + pin holes.
enable_upnp=False
)
node.add_msg_cb(msg_cb)
#
# Spawn a new pipe from a P2P con.
# Connect to our own node server.
pipe, success_type = await node.connect(node.addr_bytes)
pipe.subscribe(SUB_ALL)
#
# Test send / receive.
msg = b"test send"
await pipe.send(b"ECHO " + msg)
out = await pipe.recv()
#
# Cleanup.
assert(msg in out)
await pipe.close()
await node.close()
# Run the coroutine.
# Or await example() if in async REPL.
if __name__ == '__main__':
async_test(example)
You can use this library as a black box if you want. The code automatically handles loading network interfaces, enumerating routers, bypassing NATs, and establishing P2P connections. But you can do far more with P2PD.
It can be used as a way to do network programming in general. Whether you want to write multi-protocol, multi-address clients or servers. Using P2PD makes this simple. And it supports either using async or sync callbacks or a pull / push style API. Something like the Python equivalent of ‘protocol classes’ versus ‘stream reader / writers’ but with more control.
- IPv6-specific socket bind code
Link-local address logic
Global-address logic
Platform-specific logic
- Interface-specific connection addresses
Different for IPv6 link local addresses
Different for IPv6 global addresses
Interface support (in general)
External address suport
- Whether to use ‘protocol’ classes or ‘streams’
Protocols = events; streams = async push and pull.
Python doesn’t have an async push and pull API for UDP at all because Guido van Rossum thought it was a bad idea. I don’t agree. P2PD can do async awaits on UDP. Or it can do event-based programming like the protocol class.
Message filtering (useful for UDP protocols)
Multiplexing-specific logic for UDP
- Some very smart hacks to reuse message handling code
Python has radically different approaches for TCP cons and servers.
While it does not provide the same methods for UDP.
I’ve created software that provides the same API features whether its a server or connection; TCP or UDP; IPv4 or IPv6
- Basics
- Pipes
- TCP echo server example
- UDP await example
- Pipe methods
- def subscribe(self, sub=SUB_ALL, handler=None)
- def unsubscribe(self, sub)
- async def recv(self, sub=SUB_ALL, timeout=2, full=False)
- async def send(self, data, dest_tup=None)
- def add_msg_cb(self, msg_cb)
- def del_msg_cb(self, msg_cb)
- def add_end_cb(self, end_cb)
- def del_end_cb(self, end_cb)
- def add_pipe(self, pipe)
- def del_pipe(self, pipe)
- async def close(self)
- Additional pipe options
- Queues
- Daemons
- TURN client
- STUN client
- Examples