Skip to main content

Asyncio UDP server with traffic throttling

Project description

Asyncio UDP server

Build Status

Yet another async UDP server. Based on bare sockets.

Killer-feature: bandwidth throttling for uploading & downloading.

UDPServer.__init__

UDP server constructor.

Arguments:

  • upload_speed — maximum outgoing traffic speed in "byter per second" (default: 0 — unlimited);
  • download_speed — maximum incoming traffic speed in "byter per second" (default: 0 — unlimited);
  • recv_max_size — maximum socket window size for receiving (default: 262144).

UDPServer.run

Server startup function.

Arguments:

  • host — socket bind host/interface (e.g. "0.0.0.0", "127.0.0.1" or "localhost");
  • port — socket listen port;
  • loop — event loop.

UDPServer.send

Enqueue data for sending.

Arguments:

  • databytes or bytearray data for sending;
  • addr — tuple of host and port (e.g. ("127.0.0.1", 9876) or ("some.domain.com", 5556)).

UDPServer.subscribe

Subscribe coro or future on a datagram received event.

Arguments:

  • fut — coroutine or future with arguments data and addr.

UDPServer.unsubscribe

Unsubscribe coro or future from a datagram received event.

Arguments:

  • fut — coroutine or future.

Virtual events

  • UDPServer._connection_made() — virtual method, call after socket bind successfully;
  • UDPServer._socket_error(data, addr) — virtual method, call when got socket error;
  • UDPServer._datagram_received(data, addr) — virtual method, call each time when server got incoming data, can be used for modify data before pass it to subscribers;
  • UDPServer._notify_subscribers(data, addr) — virtual method, call with params returned from _datagram_received.

Example

import asyncio
import datetime

from aioudp import UDPServer


class MyUDPServer:
    def __init__(self, server, loop):
        self.server = server
        self.loop = loop
        # Subscribe for incoming udp packet event
        self.server.subscribe(self.on_datagram_received)
        asyncio.ensure_future(self.do_send(), loop=self.loop)

    async def on_datagram_received(self, data, addr):
        # Override virtual method and process incoming data
        print(datetime.datetime.now(), addr, data)

    async def do_send(self):
        while True:
            # Any payload
            payload = b'd1:ad2:id20:k\xe7\x90\xcd\x0c_R\xfe\x82\xeb\xa8 x\x14\xb4-\x8e0\xe5\x086:target20:\x11\x8e\xcc,\x89\xa4\x99\xf98E\x98\x7f!\xa7w\rz\x1b\x14de1:q9:find_node1:t2:#K1:y1:qe'
            # Delay for prevent tasks concurency
            await asyncio.sleep(0.001)
            # Enqueue data for send
            self.server.send(payload, ("router.bittorrent.com", 6881))

async def main(loop):
    # Bandwidth speed is 100 bytes per second
    udp = UDPServer(download_speed=100, upload_speed=100)
    udp.run("0.0.0.0", 12346, loop=loop)

    server = MyUDPServer(server=udp, loop=loop)


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main(loop))
    loop.run_forever()

Output:

2019-06-04 16:04:07.761576 ('67.215.246.10', 6881) b'd2:ip6:Z\x97\'X0:1:rd2:id20:2\xf5NisQ\xffJ\xec)\xcd\xba\xab\xf2\xfb\xe3F|\xc2g5:nodes416:\x02\xf7\xb7\x1d\x94\xb1\x00\xe8\x84\xca\x9c\xc3\xb09\xbb\x04*P\x8cQ\x1f\x7fF\xa9\x1a\xe1\x00\x13\x99W\x14\xb1\x99\x8b\xff\x8e\xda2\xb3 \xac\x82\xef0sCl\xda+G\xc4\x91\x17bO,\xe8\xbc\x9e\xeb+\xfc.\xe6\xf8\xb0\x82\xee~\x17 \xebvF\x83\xf8\x83\xc1\xdaK\x1b\x83\x17\xfb!\xab\x1e\x97T\xa3\xfa\x9a\x14Q$\x06)\\\xad>O\x03\xc4\x91\x9f/7\xf6>]|\xe0\xc6f\x1eq\xfd\xcc\x0e\xbe\xd0\x85\xde\xf9\xbeJc\xe3e\xe5\x9e\x9d\xca})P\xa5\xfd\x8c\xb66\xb9\xba\x0f\xfc\xb1\xb9\xa3 E\xc9*\x1f\xd1I\xb1r9\xfaI\xf1\xf1\xbb\xe9\xeb\xb3\xa6\xdb<\x87\x0c>\x99$^R\xbc\x85\xd3\xb3C\xab}\xbf6\xcc\x11\x7fv\xde\r\xed\xa4q\xdd\x04#\x8d\xd8j7$[\xe5e\\\xf9V\x166\xb1P\x7f\xa01v\xf49\xb9\x9a\x12*\xad<Q@\xa6\x8d\xd5\xe6wN\xa2\x87\xaf|\xb4KslP\xf5\xd0h\xaf\x1c\xb5\xb6\xc0\xc9\xfa\x0c\xfe\xe8\xb92\n\xb7\xda"8\x019\x95\xe8\xef\xc1u\xa4$\xe5\x0c!\xe2\x10j\xff\x10\xeb\xffu\x02\x87t\x9f\x01|\xa4b\x87\xc4\xa0.\xd2g\x9f\xf6\xd1\xc2\xfd\xef\xa1!a\x04\xdf<\x97)\xbe\xd1\x81l\xb1?W\t\xbb\xc1\xbd\x04\x8ec\xf8\x08\xcd\x1av\x1a\xa9\xb3R^\xb5{\x11\x1a\xe1C\xe1\xb43\x04\x03Wl\xd1E\xa6\x0f\xf7\xcbQ\t\x84W\xd6\xebD\'\xdd=\xb1\xc9\xed\xc6C\xf9\x7f\xfbg\xf9$\x0e\x0b~\xa5\xae\xfdnvr\xe7\xc4.\xf8(\x90\xa4\x84C\xe9\xccK4r\xc6=\xf1\xc09\xdf{\xfc\x10\xe9\xd9\xd8\xa5\xdd.\x00\x88\x0e\xe7]e1:t2:#K1:y1:re'
2019-06-04 16:04:12.622861 ('67.215.246.10', 6881) b'd2:ip6:Z\x97\'X0:1:rd2:id20:2\xf5NisQ\xffJ\xec)\xcd\xba\xab\xf2\xfb\xe3F|\xc2g5:nodes416:\x1e\xe4\xb8\x96\xf5E;\x13\r\x89\n\x1c\xdb\xae2 \x9aP\xee\xcb\xa0\xb0\xcbq\xa4X\x80\x8c>\x91:M\xb67%kY\xc7\xb5\xe1\xe7\x901_u\\\xbd(g\xeb[\xa7-\x1fl\x1e\xd6\x93\xe8g\xdam\n;\xa8y \xf4K\x18\xe0\x11z\xfe\xa6\xf6\xc9\xcfe\xcb\xf9\x7f+"92\xfb/\xd8C>K\x1a\xf9\xe8#\x86\x8dy\x07\xa1\x94>\x81\x17_\xcd\xe9\x03?\x96pX\x1c\xe8v{\xa5W,_ \x0f|Oi\x8bg\xc5J\xfd#\x9e\xf7\xdd\xc4\xa4\xb9\xd0\x8f\xbd\x9d\x98\xfaD\x91N}U{j\xd7\xfak\')\xcdr(\xc3\xdc\xc6\xd7\x9b=\x1d\x12/\xa4\xd5A\x8f[\x98\x07\x10EHq#\xcc\xb8\xae\xf00\xd4\xfd(\xe5fj\xbb\xdd\xa5s\x8b\xec:\xb1\x1d\x93%)S\xdf.\xb3\x17\x95\x9b\xc8\xc0\x95"\x86\x81\x16\x8d]\xdau\x15S\xbcN:\x9d\x83I+Vk\xc4\x91\x0b9t\xbdv`\xb2\x8d7\xae\t\xb1\x8c\x96\x9f\x1c\xa0B\xd0\x93\xbfd\x8e\xe7\xdf`\x14\x13\'<\x92Pn:u\xa0\n\xc3\xcd\x01\x12H\xc65\xeb\xa2X\xc8\xd6\x0b\x81\x1b\xcf\x89\xbco$\x8a\xf97\x920u\xb8i\xde\x15\x19\xc4\x8f\xfb\xc0\xb1\xcf\x99\xd9e1\xf6u\x01\xb7\x1e\xc4\x80\xca\x0e\xcawuv\xc9\xaf[\x0b\xd4\x99\xf4[\xf1\x8ck\x1a\xe1r\xc3\xb4(\xb8ba\x98\x8b\x1e\xb4TN"\xa7F5B\xcb\x0f\x05\x9e\xed-b\x82\xb8\x8d\xed\xf7\xaf?,\xc5)z\xd2f\xc1\xda`.\xf0.!\xf2H\xbezs\xf3\x8c\t\x9c\x9d\xe8\x87Q\xf4\xe9\x8fJ\x0c\x16\xa8\xc55Q\x878<\x04.0Bc!\xade1:t2:#K1:y1:re'
2019-06-04 16:04:17.483860 ('67.215.246.10', 6881) b'd2:ip6:Z\x97\'X0:1:rd2:id20:2\xf5NisQ\xffJ\xec)\xcd\xba\xab\xf2\xfb\xe3F|\xc2g5:nodes416:k*[\x0c1\x9a\xb1\xd6\xe5>G\x0c\xac\xe3v0\xd0\xc2\xb7\x8cI\x93\xb7\xcd\xa8\xf1\xca(\xf8\xc0\xe0\xa6\xd4\xda*\xd4\xf1\xedc\xb4\xdf\xdf\xe2Q\x1a\xe9^\t\x8f\xfa\x1a\xe1$Wr\xe18\x1e\x9a\xce\xabW}\xbfLY\x05\xbd\xce\xa0\x0e\xb1_\x19\xdf>\xcaJS\x00H\xdf\x8d\xeedn\xd7\x07-\x9f\x8at\xb0\x14\xbe\x8d\x1d\x0eO\xa7\x97\xa3\x83\xa9\xb2\xff\xc9K\x97\x01m\xbc\xcb\xc3\x001vR\'s\xffyr\x82UF\\z\xafG\x8f\xc8U\xcf}+\x97\x90\xa2\x1b\x11\xef\xbfs\x80Q\xff\x1dc\x14%\xe8\x87\xac!\xadz\xbd\xcc\xbfq\xaex0\x7f(}jY[W\xdcsx\n|F\x86\xed\xa3\x1a\xe1,\x03\x87to\xd2X\x12\x00\x90\t\xcfu\xec_m\x15\x02\xe1\xf6\xd4\x98\x93\xfa\x1a\xe1\xc8\xa1-tC!\xd1X\xb0\xc9M\xc8\xf2\x94|\xca\x80j\xb8\x04\x05\xceB"_\xc3H\xf1i\x86\xb6\x07Z=\xc2\xb1`m\xab\xbc\xbeB\xb8\nVM]Q\x1cp\xc2L\x83\xca\xe7\xed\x1e\x10\x9d\xc9\xde\xdc[c\xef\xc2_]+N\xaaJ\xc9E0\xf5\x1b\x16q\xe1/\x97w>a\x94\x10\x0f\x94\xc2"\xd6\x89+\xa7\x01\\w%\x90Z_\x1a\xe1\xd9\xf5\xff\xb5D\xf2\x8d\xbc^[j\x16\xb9H\xe3.+\r\x87\xa5aU\xafMT\xec\x80\x02\x9bP\xf5\xc7\xe9\x8e\x9e\x94\x96\xc8\xcdu\xa1\xc1.\xa7 \xb5I\x0b\x9d\xef\xd0\x8ff\x91+\xc2\xe0\xeb/\xbe\x05\x87\xac\xd8\xb8vA\xe8?V\xa3\xfdJ\xc1\x9f\xe8\xc4\x91=\xc4\x16H\xbdP\x1fv5x9\x02\'N]Zh\xed\xc9\xc3\xb5\xae:\x04\xc8\x04e1:t2:#K1:y1:re'
2019-06-04 16:04:22.345648 ('67.215.246.10', 6881) b'd2:ip6:Z\x97\'X0:1:rd2:id20:2\xf5NisQ\xffJ\xec)\xcd\xba\xab\xf2\xfb\xe3F|\xc2g5:nodes416:+S\xdc\x8c\xc8%~ \x12\xd3\xd9@\xa9X4\x9at\xcc\x8a\xf9z\x08\x1d\x16h/\xf2}\xb7I\xd9\x8fP\x81(\x9d\xed\xae\xa5\xe5\xe4S1,\xf0-\xaeavL\xc4\x91~\xd5\x01`o\xa8\xc4\xe0\xfa \xa5\xf0\xf1~`\x0c\x19[+\x85\xbb\xd0\x0bp\xf3\x8c\xe98S\xcd`=n|\xa8\x8c\xa4~\xfc\xedg\xce\xff\x8f\x13}g\xfc\xc9)\xa8\x89L&]\\\xb3M\xc6NUE\x7f\xb8CQ\x82\xe1{C\xba\xaf\x884g\x9a\xf3\x8c\x1a\xe0\xbb\x1a\x12\xafCQ\x1e\x18\x91;\x80P\xee\xf0\xdc\t\xc1m.\xe4\x04\xda1\xff\x93?\xa0r\xc5\x1d\x9c\x1f\xc7\xf2\x0e[\xc9\tV4\x94\x02w\xc2\\b\x03+\x1a\xe1\xc4\x80#z0\x14\x9d}Kj\xbe\xf2>\xa5\xf8k\x12\x9a|u\xb0:\xc1,U\r\xc7kE\xa1UY\x1e~:r\x14\xb6p&\xd7\xdf\xaf>\x9b\xa5b\xb5\xcd\x1f\xc4\x91Z\xdf\x1a\x1f\xdb\x9a\x84\tu\xff\xda(\xddqy}nT\xaa\x8f%\x06.\xc9\xda\xe4\xc0\x9a\x03\x9c\x12?\xf1s[\xb1\xd7;\xe3)\xb9[o\r\xb6=\xb57\x98&\xc4\x91.\xd1\x87)5\xf5s\xb8\xc9z\xc4\x9d\x063Kw\xaf\xcc\x046\xdbN\xcf,\xf3\x8c\xaa\x85\xee\xd0e\xfa\xca\xd2\x93\x82\xe2\x98\xeaA\xdc\xbd\xaf\x97F\xe9r\x19\xb4i\xde~>\x82\xfb\xe9y#vJ\xe4\x8e\x0e\xd1\xee]\xe0qk(\xb2\x92\x95\xff\x02\n\x98\x9c"\xcaY\x8d\xb6\x81a\xa1\xf9\xc1\nX\xb4\x9e\xcfo\xbf\xa3j\x8fH1\'7\xc4\x91v\xd1_\xec\xe4\xe1\x1f\xcb\xd5\xbd\xf43\x0c\'"\x04\xa5\xa2\x0f\xd0\x05>+\xb1 Xe1:t2:#K1:y1:re'

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Files for aio-udp-server, version 0.0.6
Filename, size File type Python version Upload date Hashes
Filename, size aio_udp_server-0.0.6-py3-none-any.whl (19.1 kB) File type Wheel Python version py3 Upload date Hashes View
Filename, size aio-udp-server-0.0.6.tar.gz (6.5 kB) File type Source Python version None Upload date Hashes View

Supported by

Pingdom Pingdom Monitoring Google Google Object Storage and Download Analytics Sentry Sentry Error logging AWS AWS Cloud computing DataDog DataDog Monitoring Fastly Fastly CDN DigiCert DigiCert EV certificate StatusPage StatusPage Status page