Jump to content
Main menu
Main menu
move to sidebar
hide
Navigation
Main page
Recent changes
Random page
Help about MediaWiki
Matthews Lab
Search
Search
Appearance
Log in
Personal tools
Log in
Pages for logged out editors
learn more
Contributions
Talk
Editing
P2pd
(section)
Page
Discussion
British English
Read
Edit
Edit source
View history
Tools
Tools
move to sidebar
hide
Actions
Read
Edit
Edit source
View history
General
What links here
Related changes
Special pages
Page information
Appearance
move to sidebar
hide
Warning:
You are not logged in. Your IP address will be publicly visible if you make any edits. If you
log in
or
create an account
, your edits will be attributed to your username, along with other benefits.
Anti-spam check. Do
not
fill this in!
= P2PD async networking library = The first problem I wanted to solve was the lack of network interface support. In network programming it’s common to see code that glosses over interface management. What makes this so appealing is the operating system supports default interfaces. So why bother choosing one? The problem is: if you write code that only uses the default interface then your program won’t be able to utilise all routes – possibly an issue for some software. <syntaxhighlight lang="python">from p2pd import * async def main(): # Select interface and choose a route to bind to. # No interface name = default interface. i = await Interface() route = i.route() # You can also load the NAT details. await i.load_nat() print(i) # All addresses and NAT behaviour</syntaxhighlight> Previously, I spoke about how the asyncio module only provides async functions for TCP. I thought this was a major limitation. So I added support for async UDP. Obviously UDP isn’t reliable so sometimes recv calls time out. But importantly – doing I/O isn’t going to block the program so the event loop will be free to work on other tasks. <syntaxhighlight lang="python"> # Open UDP endpoint. await route.bind() # port=0 ... dest = await Address("p2pd.net", 7, route) pipe = await pipe_open(UDP, dest, route) pipe.subscribe() # Async networking. await pipe.send(b"echo back this message.", dest.tup) out = await pipe.recv(timeout=2) print(out) # Cleanup the endpoint. await pipe.close() # More info on the basics here: # https://p2pd.readthedocs.io/en/latest/python/basics.html</syntaxhighlight> A key goal I had for this library was to provide the same APIs for most use-cases. No matter if the transport is TCP or UDP; IPv4 or IPv6; Server or client. The main abstraction I use is called a ‘pipe.’ Pipes allow developers to choose what programming model to use. They support async coroutines and event-based callbacks. My library fully supports using coroutines as callbacks or regular functions. <syntaxhighlight lang="python"> async def msg_cb(msg, client_tup, pipe): await pipe.send(msg, client_tup) # Adds a message handler before pipe creation. # Can use callbacks or awaits. pipe = pipe_open(TCP, route=route, msg_cb=msg_cb) # Alternatively you can use add_msg_cb. pipe.add_msg_cb(msg_cb)</syntaxhighlight> Some servers need to support multiple protocols and address families. For this there is the Daemon class. There’s not much to it. It simply handles creating pipes for you. <syntaxhighlight lang="python">class EchoServer(Daemon): def __init__(self): super().__init__() async def msg_cb(self, msg, client_tup, pipe): await pipe.send(msg, client_tup) async def main(route): await route.bind(port=12345) server = await EchoServer().listen_all( [route], [12345, 8080], [TCP, UDP], af=AF_ANY )</syntaxhighlight> You’ll notice that the only way to build servers in Python is with callbacks. I think this is usually a good model. But what if you want to write an async version? That is – what if you want to await accepting a new client? Well, now you can. Simply await the pipe and it will return a regular pipe for the next client that connects to the server. <syntaxhighlight lang="python">client = await pipe await client.send(b"hello")</syntaxhighlight> Currently, my examples have been ‘push’ and ‘pull.’ However, sometimes it’s useful to be able to subscribe to certain messages. Consider UDP for a moment. In UDP messages may arrive in any order. Therefore, it’s common to see protocols using unique IDs in response messages that mirror the IDs used in requests. What this means is ideally it should be possible to subscribe to certain patterns and await the results. That’s how async recv works in P2PD. You subscribe using a regex pattern and await a response. If you look at the async examples earlier you may see I called subscribe(). What this means is ‘subscribe to all messages.’ Any message matching that pattern will be added to their own queue. You can then await that queue using the recv() call. It’s very flexible. More info on that here: https://p2pd.readthedocs.io/en/latest/python/queues.html <span id="peer-to-peer-networking-with-p2pd"></span>
Summary:
Please note that all contributions to Matthews Lab may be edited, altered, or removed by other contributors. If you do not want your writing to be edited mercilessly, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource (see
Matthews Lab:Copyrights
for details).
Do not submit copyrighted work without permission!
Cancel
Editing help
(opens in new window)