Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.python > #81631 > unrolled thread
| Started by | Benjamin Risher <brisher777@gmail.com> |
|---|---|
| First post | 2014-11-27 06:27 -0800 |
| Last post | 2014-11-29 00:04 +0300 |
| Articles | 4 — 2 participants |
Back to article view | Back to comp.lang.python
Asyncio problem, looking for advice. Benjamin Risher <brisher777@gmail.com> - 2014-11-27 06:27 -0800
Re: Asyncio problem, looking for advice. Akira Li <4kir4.1i@gmail.com> - 2014-11-28 15:10 +0300
Re: Asyncio problem, looking for advice. Benjamin Risher <brisher777@gmail.com> - 2014-11-28 07:51 -0800
Re: Asyncio problem, looking for advice. Akira Li <4kir4.1i@gmail.com> - 2014-11-29 00:04 +0300
| From | Benjamin Risher <brisher777@gmail.com> |
|---|---|
| Date | 2014-11-27 06:27 -0800 |
| Subject | Asyncio problem, looking for advice. |
| Message-ID | <574feffa-6412-4ae3-8de0-f84b0bdba9d2@googlegroups.com> |
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.
Here's a link to the same code as below, just with syntax highlighting etc...
http://pastebin.com/iLE4GZH3
import asyncio
import logging
class MyServer:
def __init__(self, loop):
self.server = None
self.loop = loop
self.clients = dict()
self.log = logging.getLogger(__name__)
def _accept_client(self, client_reader, client_writer):
""" Client initially drops in here """
task = asyncio.Task(self._handle_client(client_reader, client_writer))
self.clients[task] = (client_reader, client_writer)
def client_done(task):
self.clients[task][1].close() # closes the StreamWriter that was part of the task
del self.clients[task]
self.log.info("New connection.")
task.add_done_callback(client_done)
@asyncio.coroutine
def _handle_client(self, client_reader, client_writer):
""" Try to connect to port 22 and broker between 2 connections.
:param client_reader: StreamReader object
:param client_writer: StreamWriter object
"""
reader, writer = yield from asyncio.async(asyncio.open_connection('localhost', 22))
while True:
client_data = yield from asyncio.async(self.read_data(client_reader))
if client_data:
writer.write(client_data)
yield from writer.drain()
server_data = yield from asyncio.async(self.read_data(reader))
if server_data:
client_writer.write(server_data)
yield from client_writer.drain()
@asyncio.coroutine
def read_data(self, reader):
data = None
while True:
print('top of while')
data = yield from asyncio.Task(reader.read(2048))
return data
def start(self):
""" start the server listening on 12345 """
self.server = self.loop.run_until_complete(
asyncio.streams.start_server(
self._accept_client, # client_connected_callback
'localhost', # host
12345, # port
loop=self.loop # loop
)
)
def stop(self):
if self.server:
self.server.close()
self.loop.run_until_complete(self.server.wait_closed())
self.server = None
if __name__ == '__main__':
loop = asyncio.get_event_loop()
my_server = MyServer(loop)
my_server.start()
loop.run_forever()
my_server.stop()
[toc] | [next] | [standalone]
| From | Akira Li <4kir4.1i@gmail.com> |
|---|---|
| Date | 2014-11-28 15:10 +0300 |
| Message-ID | <mailman.16402.1417176727.18130.python-list@python.org> |
| In reply to | #81631 |
Benjamin Risher <brisher777@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
[toc] | [prev] | [next] | [standalone]
| From | Benjamin Risher <brisher777@gmail.com> |
|---|---|
| Date | 2014-11-28 07:51 -0800 |
| Message-ID | <cd749d53-9bbf-4cc0-9997-f28ceb995607@googlegroups.com> |
| In reply to | #81677 |
On Friday, November 28, 2014 6:12:20 AM UTC-6, Akira Li wrote: > Benjamin Risher 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] > https://docs.python.org/3/library/asyncio-task.html#example-parallel-execution-of-tasks > > [2] http://pastebin.com/g08YaJyz > > > -- > Akira Akira, First, thank you very much for your response. It helps tremendously. I have a question or two though, if you don't mind. You said Task() is deprecated, but it's not listed as such in the docs. Is it just that it's preferred to use other methods instead of using Task() directly, or am I missing something? Also, just so I can wrap my head around things, I was trying to mix concurrent and procedural programming in the code I provided, correct? Do you have any advice on how to avoid mixing types again in the future? Thank you again, Ben
[toc] | [prev] | [next] | [standalone]
| From | Akira Li <4kir4.1i@gmail.com> |
|---|---|
| Date | 2014-11-29 00:04 +0300 |
| Message-ID | <mailman.16417.1417208785.18130.python-list@python.org> |
| In reply to | #81688 |
Benjamin Risher <brisher777@gmail.com> writes:
> On Friday, November 28, 2014 6:12:20 AM UTC-6, Akira Li wrote:
>> Benjamin Risher 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]
>> https://docs.python.org/3/library/asyncio-task.html#example-parallel-execution-of-tasks
>>
>> [2] http://pastebin.com/g08YaJyz
>>
> Akira,
>
> First, thank you very much for your response. It helps tremendously.
> I have a question or two though, if you don't mind.
>
> You said Task() is deprecated, but it's not listed as such in the
> docs. Is it just that it's preferred to use other methods instead of
> using Task() directly, or am I missing something?
asyncio is a provisional API [3], it may change without notice (though
it shouldn't without a reason). From asyncio docs [4]:
Don’t directly create Task instances: use the async() function or the
BaseEventLoop.create_task() method.
The reason is probably to support Trollius (asyncio for Python 2) [5].
> Also, just so I can wrap my head around things, I was trying to mix
> concurrent and procedural programming in the code I provided, correct?
> Do you have any advice on how to avoid mixing types again in the
> future?
In short, the main issue was that your code executed some parts
sequentially e.g., A then B:
yield from A
yield from B # this is not reached until A finishes
that needed to be run concurrently:
yield from asyncio.wait([A, B])
or in general, if you don't need to wait the results:
asyncio.async(A)
asyncio.async(B)
# ... yield later, to pass the control to the event loop
(Task._step/_fut_waiter dance ☯)
Ask, if you have any specific questions about the code
http://pastebin.com/g08YaJyz
There is a bug at the end. info('done') should be outside the loop
(improper indent).
[3] https://www.python.org/dev/peps/pep-0411
[4] https://docs.python.org/3/library/asyncio-task.html#asyncio.Task
[5] https://code.google.com/p/tulip/issues/detail?id=185
--
Akira
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.python
csiph-web