Bienvenue sur IndexError.

Ici vous pouvez poser des questions sur Python et le Framework Django.

Mais aussi sur les technos front comme React, Angular, Typescript et Javascript en général.

Consultez la FAQ pour améliorer vos chances d'avoir des réponses à vos questions.

Lier un server et un client tcp avec asyncio.Protocol

+2 votes

Je cherche à écrire un proxy TCP destiner a maintenenir un comportement existant tout en dupliquant les messages reçus ailleurs.

Tout d'abord, un petit server Echo qui servira de point de sortie du proxy

#!/usr/bin/python3

import asyncio
import signal

name = 'OutServer'

class OutServer(asyncio.Protocol):

    def connection_made(self, transport):
        print('%s: connection made' % name)
        self.transport = transport

    def data_received(self, data):
        print('%s: data received: %r' % (name, data))
        self.transport.write(b'Re: ' + data)

    def eof_received(self):
        pass

    def connection_lost(self, exc):
        print('%s: connection lost: %s' % (name, exc))

def exception_handler(loop, context):
    print('%s: %s' % (name, context['exception']))

loop = asyncio.get_event_loop()
loop.add_signal_handler(signal.SIGINT, loop.stop)
loop.set_exception_handler(exception_handler)
f = loop.create_server(OutServer, '127.0.0.1', 3333)
loop.run_until_complete(f)
print('Server running on %s:%d' % ('127.0.0.1', 3333))

try:
    loop.run_forever()
finally:
    loop.close()

Ensuite le proxy:

#!/usr/bin/python3

import asyncio
import signal
import traceback


class ProxyIn(asyncio.Protocol):
    name = 'ProxyIn'

    def __init__(self, proxy_out_addr):
        self.proxy_out_addr = proxy_out_addr

    def connection_made(self, transport):
        print('%s: connection made' % self.name)
        self.transport = transport
        asyncio.ensure_future(self.proxy_out_connect(*self.proxy_out_addr))

    def data_received(self, data):
        print('%s: data received: %r' % (self.name, data))
        self.proxy_out_transport.write(data)

    def eof_received(self):
        pass

    def connection_lost(self, exc):
        print('%s: connection lost: %s' % (self.name, exc))

    async def proxy_out_connect(self, host, port):
        self.proxy_out_transport, proxy_out = await loop.create_connection(ProxyOut, host, port)
        proxy_out.proxy_in_transport = self.transport


class ProxyOut(asyncio.Protocol):
    name = 'ProxyOut'

    def connection_made(self, transport):
        self.transport = transport
        print('%s: connection made' % self.name)

    def data_received(self, data):
        print('%s: data received: %r' % (self.name, data))
        self.proxy_in_transport.write(data)

    def eof_received(self):
        pass

    def connection_lost(self, exc):
        print('%s: connection lost: %s' % (self.name, exc))


def exception_handler(loop, context):
    print('%s: %s' % ('Proxy', context['exception']))
    traceback.print_exc()


proxy_in_addr = ('127.0.0.1', 4444)
proxy_out_addr = ('127.0.0.1', 3333)

loop = asyncio.get_event_loop()
loop.add_signal_handler(signal.SIGINT, loop.stop)
loop.set_exception_handler(exception_handler)
f = loop.create_server(lambda: ProxyIn(proxy_out_addr), *proxy_in_addr)
loop.run_until_complete(f)
print('Server running on %s forwarding to %s' % (proxy_in_addr, proxy_out_addr))

try:
    loop.run_forever()
finally:
    loop.close()

Maintenant il suffit de claquer un "telnet 127.0.0.1 4444" et de constater que ca marche.

En fait ca marche par hasard, car le temps de taper qqch dans le telnet, la connection est établie coté ProxyOut donc self.proxyouttransport est dispo et le write passe.

Si on écrit un petit client qui bourrine des messages immédiatement:

#!/usr/bin/python3

import asyncio
import time
import uuid

@asyncio.coroutine
def InClient(loop, host, port):
    reader, writer = yield from asyncio.open_connection(host, port, loop=loop)

    #time.sleep(5)

    while True:
        data = uuid.uuid4().bytes
        print('InClient: Sending: %r' % data)
        writer.write(data)
        time.sleep(0.1)

        data = yield from reader.read(1024)
        print('Inclient: Received: %r' % data)

    print('InClient: Close the socket')
    writer.close()

host = '127.0.0.1'
port = 4444

loop = asyncio.get_event_loop()
print('InClient: connection to %s:%d' % (host, port))
loop.run_until_complete(InClient(loop, host, port))
loop.close()

Ca nous donne:

Server running on ('127.0.0.1', 4444) forwarding to ('127.0.0.1', 3333)
ProxyIn: connection made
ProxyIn: data received: b'\x18\xdfu2\t\xd7D\xfb\xa8\xc0\x08\xbdU\xd3+x'
Proxy: 'ProxyIn' object has no attribute 'proxy_out_transport'
Traceback (most recent call last):
  File "/usr/lib/python3.5/asyncio/events.py", line 126, in _run
    self._callback(*self._args)
  File "/usr/lib/python3.5/asyncio/selector_events.py", line 730, in _read_ready
    self._protocol.data_received(data)
  File "./proxy_simple.py", line 21, in data_received
    self.proxy_out_transport.write(data)
AttributeError: 'ProxyIn' object has no attribute 'proxy_out_transport'
ProxyOut: connection made
ProxyIn: connection lost: None

data_received est appelée sur ProxyIn avant que ProxyOut soit connecté.

J'ai essayé des tones de trucs en m'inspirant de ce que je trouve sur le net mais je n'ai JAMAIS réussi a bloquer dataread tant que proxyout_connect n'est pas fini.

Pourriez-vous m'aider ? Bien entendu je n'y connais absolument rien à asyncio, je suis même pas dev...

demandé 28-Jan par acecile (120 points)
edité 28-Jan par max

Votre réponse

Preview

Votre nom à afficher ( en option ):
Vie privée: . Votre adresse de messagerie ne sera utilisée que pour l'envoi de ces notifications .
Vérification anti -spam:
Pour éviter cette vérification à l'avenir, Connectez vous ou inscrivez vous.
...