Asyncio problem, looking for advice.
Akira Li
4kir4.1i at gmail.com
Fri Nov 28 07:10:35 EST 2014
Benjamin Risher <brisher777 at gmail.com> writes:
> Hello all,
>
> I'm working on a project to learn asyncio and network programming. What I'm trying to do is forward a connection from myself to another machine. Kind of like an asynchronous python implementation of fpipe.
>
> In a nutshell:
>
> 1 --> start a server listening on localhost
> 2 --> connect to server
> 3 --> server connects to a listening service (ssh for instance)
> 4 --> server handles both connections to pass traffic back and forth through each
>
> What I have now *kind of* works. It sends data back and forth, but when I ssh to localhost -p 12345, i never get the password prompt. It looks like one packet hangs out and doesn't get sent from what I saw in tcpdump.
>
> Any help would be greatly appreciated.
Do you want to emulate `ssh -L 12345:localhost:22 <host>`?
> Here's a link to the same code as below, just with syntax highlighting etc...
> http://pastebin.com/iLE4GZH3
There are several issue e.g., unnecessary async(), deprecated Task()
calls but the main issue is that _handle_client() doesn't read
concurrently from the server while client writes to it.
You could use asyncio.wait() to run several tasks in parallel
[1]. Here's a forward-port.py example [2]:
#!/usr/bin/env python3
"""Forward a local tcp port to host:port.
Usage: %(prog)s local_port:host:port
Example:
$ python3 forward-port.py 26992:icanhazip.com:80 # start server
and in another window:
$ curl localhost:26992 # connect to it
"""
import asyncio
import logging
import sys
info = logging.getLogger('forward-port').info
@asyncio.coroutine
def copy_stream(reader, writer, bufsize=1<<16):
while True:
data = yield from reader.read(bufsize)
if not data:
break
writer.write(data)
yield from writer.drain()
writer.close()
def port_forwarder(host, port, *, loop):
@asyncio.coroutine
def forward(local_reader, local_writer):
client = local_writer.get_extra_info('peername')
info('connected client %s %s', *client)
remote_reader, remote_writer = yield from asyncio.open_connection(host, port, loop=loop)
yield from asyncio.wait([copy_stream(local_reader, remote_writer),
copy_stream(remote_reader, local_writer)],
loop=loop)
info('disconnected client %s %s', *client)
return forward
# main
logging.basicConfig(level=logging.INFO,
format="%(asctime)-15s %(message)s", datefmt="%F %T")
if len(sys.argv) != 2:
sys.exit(__doc__)
local_port, host, port = sys.argv[1].split(':') # e.g., 12345:localhost:22
loop = asyncio.get_event_loop()
server = loop.run_until_complete(asyncio.start_server(port_forwarder(host, int(port), loop=loop),
'localhost', int(local_port), loop=loop))
info('listening on: %s %s', *server.sockets[0].getsockname())
for closing in range(2):
try:
loop.run_until_complete(server.wait_closed())
except KeyboardInterrupt:
if not closing:
server.close()
info('closing server')
else:
break
info('done')
loop.close()
[1]
https://docs.python.org/3/library/asyncio-task.html#example-parallel-execution-of-tasks
[2] http://pastebin.com/g08YaJyz
--
Akira
More information about the Python-list
mailing list