简体   繁体   中英

Python Twisted best way to signal events to a proxy

I will be hosting a service that will be acting somewhat like a proxy for something I am a client to.

So I want my ProxyService (a twisted.protocol server) to takes lots of actors (clients). On the server side of things, I need a global connection ( only need 1 connection to it for all clients ) to an ExistingService (code I didn't write, and I'm a client to it).

  • When the ExistingService says something interesting, I need to broadcast it to all actors.
  • When an actor says something to my ProxyService, I need to check if it looks good to me. If it does, I need to inform the ExistingService.

I think I know how to solve this using global variables, but just wondering if better way to push the messages.

在此处输入图片说明

You have the basic design well established. It's a basic "man in the middle" approach. There are many ways to implement it, but this should get you started:

from twisted.internet import endpoints, protocol, reactor


class ProxyClient(protocol.Protocol):

    def connectionMade(self):
        print('[x] proxy connection made to server')
        self.factory.proxy_proto = self

    def connectionLost(self, reason):
        print('[ ] proxy connection to server lost: {0}'.format(reason))
        self.factory.proxy_proto = None

    def dataReceived(self, data):
        print('==> received {0} from server'.format(data))
        print('<== transmitting data to all actors')
        for actor in self.factory.actors:
            actor.transport.write(data)


class Actor(protocol.Protocol):

    def connectionMade(self):
        print('[x] actor connection established')
        self.factory.actors.add(self)

    def connectionLost(self, reason):
        print('[ ] actor disconnected: {0}'.format(reason))
        self.factory.actors.remove(self)

    def dataReceived(self, data):
        print('==> received {0} from actor'.format(data))
        proxy_connection = self.factory.proxy_factory.proxy_proto
        if proxy_connection is not None:
            print('<== transmitting data to server through the proxy')
            proxy_connection.transport.write(data)
        else:
            print('[ ] proxy connection to server has not been established')


def setup_servers():
    PROXY_HOST = '127.0.0.1'
    PROXY_PORT = 9000
    proxy_factory = protocol.ClientFactory()
    proxy_factory.protocol = ProxyClient
    proxy_factory.proxy_proto = None
    proxy_factory.actors = set()
    proxy_client = endpoints.TCP4ClientEndpoint(reactor, port=PROXY_PORT, host=PROXY_HOST)
    proxy_client.connect(proxy_factory)

    ACTOR_HOST = '127.0.0.1'
    ACTOR_PORT = 8000
    actor_factory = protocol.Factory()
    actor_factory.protocol = Actor
    actor_factory.proxy_factory = proxy_factory
    actor_factory.actors = proxy_factory.actors
    actor_server = endpoints.TCP4ServerEndpoint(reactor, port=ACTOR_PORT, interface=ACTOR_HOST)
    actor_server.listen(actor_factory)


def main():
    setup_servers()
    reactor.run()


main()

The core logic that allows the data received from the server to be proxied to actors is proxy_factory.actors = set() and actor_factory.actors = proxy_factory.actors . Most "list-like" containers, for lack of better words, are "global" and this example gives context into each connection's factory objects. When an actor connects to the server, an Actor protocol is appended to the set and when data is received, each protocol in the set will get the data. See the respective dataReceived() methods of each protocol object on how that works.

The example above doesn't use global variables at all, but that's not to say that you couldn't use them. See how far you can get using this method of passing around variables that give context into other objects. Also, certain situations weren't explicitly handled, such as caching received data in the event the server or actors haven't connected yet. Hopefully there's enough information here for you to determine the best course of action based on your needs. There's some room for streamlining the syntax to make it shorter as well.

As a side note. An alternative to global variables is picobox . It's a dependency injector library but I've found that it satisfies most my needs when I require parameters from external sources.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM