Thank you! loop.create_task does exactly what I want. main uses another level of yield from (as you explained). That way I don't use loop.run_until_complete within main. Here is my top-level code that integrates server task and client tasks and runs them in one loop:
# create client client = Client(...) # schedule client loop.create_task(main(client)) # create and schedule server server_coro = asyncio.start_server(client.handle_leecher, host='0.0.0.0', port=61328, loop=loop) server = loop.run_until_complete(server_coro) # schedule it try: loop.run_forever() except KeyboardInterrupt as e: ... finally: server.close() loop.run_until_complete(server.wait_closed()) client.shutdown() loop.close() On Tuesday, December 1, 2015 at 11:46:35 AM UTC-5, Guido van Rossum wrote: > > Hi Leslie, > > This sounds like a cool project. > > I don't know how to answer #1 (maybe 0.0.0.0 would work?) but for #2, > instead of using run_until_complete() you should probably use a combination > of coroutines and tasks. > > Inside a coroutine, if you want to wait for another coroutine, use "yield > from other_coroutine()". Since asyncio.wait() is also a coroutine you can > use that too: "yield from asyncio.wait(coros)". If you have something you > want to start without waiting for it, wrap it in a task and let go of it -- > it will run independently as long as your event loop is running. Start it > with "loop.create_task(coro)" -- or if you have a list of them, use a > for-loop ("for coro in coros: loop.create_task(coro)"). Don't use yield > from in this case. > > You'll need a main coroutine to kick everything off. If that remains > active and never returns until you want to exit the program, you can start > it with loop.run_until_complete(main_coro()). If you want to just kick it > off and let the other coroutines run free, you can start it with > loop.create_task(main_coro()); loop.run_forever(). Hit ^C to stop. > > Good luck! > > --Guido > > On Mon, Nov 30, 2015 at 9:07 PM, Leslie Klein <leslie...@gmail.com > <javascript:>> wrote: > >> I am writing a BitTorrent client using asyncio and the Streams API. I'm >> trying to work at the highest level possible. >> I have the download part working (client opens a connection to peers >> provided by tracker and downloads all pieces). Client can also upload >> pieces to connected peers, if requested. >> >> Big Question: how to listen to incoming connections from peers that join >> the swarm? The client running on a peer must be able to listen for incoming >> connections and upload pieces. >> I set up a server (following the documentation in 18.5.5.1). >> >> Question 1: I don't know what address to bind the server to. I get the >> error message "[Errno 10049] ... the requested address is not valid in its >> context" >> >> Question 2: I don't know how to integrate the server with the other tasks >> that run concurrently in the loop. (I assume this can be done with 1 loop). >> >> # tasks that open all connections >> tasks that are running until complete: >> coros = [client.connect_to_peer(peer) for peer in >> client.active_peers.values()] >> loop.run_until_complete(asyncio.wait(coros)) >> >> then... >> >> # tasks that get a single piece by getting blocks from multiple peers (if >> possible) >> coros = [client.get_piece(index, peer) for peer in peers] >> loop.run_until_complete(asyncio.wait(coros)) >> >> I can easily do the following (if I could answer Question 1), but it >> doesn't seem right (since my program should be listening to incoming >> connections, even before the client becomes a seeder.) >> >> After the client is a seeder, the program can start the server and the >> handle_leecher coroutine handles incoming connections and handshakes. >> Then the server runs forever and uploads pieces to the remote peers. >> >> Thanks. >> Leslie >> >> >> >> >> >> > > > -- > --Guido van Rossum (python.org/~guido) >