Как работают интерфейсы Twisted python Factory и Protocol?

Я изучаю Twisted, и в руководстве для начинающих часто используются Factory и Protocol в качестве примеров. Похоже, что Factory и Protocol интерфейсы не поддерживают отправку сообщений. Ожидается ли, что отправка сообщений будет реализована независимо от интерфейса протокола?

class IProcessProtocol(Interface):
    def makeConnection(process):
    def childDataReceived(childFD, data):
    def childConnectionLost(childFD):
    def processExited(reason):
    def processEnded(reason):

person Pippi    schedule 07.08.2015    source источник
comment
IProcessProtocol и IProtocol — разные интерфейсы; IProcessProtocol получает события от подпроцесса POSIX, порожденного spawnProcess, тогда как IProtocol обрабатывает один поток (обычно из сети). Поскольку это интерфейсы для получения потоков данных, сообщения являются более высокого уровня и обрабатываются в другом месте. Но неясно, что вы на самом деле спрашиваете о том, как отправлять сообщения, или чего вы не находите в документации.   -  person Glyph    schedule 08.08.2015


Ответы (1)


Видеть:

Фабрики создают экземпляры протокола.

Это означает, что фабрика будет использовать протокол, чтобы выяснить, как она должна прослушивать и отправлять данные (см. здесь, а также обратите внимание: вы также можете написать свой собственный протокол).

Эти методы доступны для Protocol:

Method  logPrefix Return a prefix matching the class name, to identify log messages related to this protocol instance.
Method  dataReceived  Called whenever data is received.
Method  connectionLost  Called when the connection is shut down.

Унаследовано от BaseProtocol:

Method  makeConnection  Make a connection to a transport and a server.
Method  connectionMade  Called when a

соединение выполнено.

И как только соединение будет установлено, мы можем сделать что-то вроде записи данных в transport:

from twisted.internet.protocol import Protocol
class SomeProtocol(Protocol):
    def dataReceived(self, data):
        print('Do something with data: {}'.format(data))

    def connectionMade(self):
        self.transport.write("Hello there")

Но подождите, откуда Protocol берет self.transport?

>>> from twisted.internet.protocol import Protocol, BaseProtocol
>>> import inspect
>>> from pprint import pprint
>>> pprint(inspect.getclasstree(inspect.getmro(Protocol)))
[(<class 'object'>, ()),
 [(<class 'twisted.internet.protocol.BaseProtocol'>, (<class 'object'>,)),
  [(<class 'twisted.internet.protocol.Protocol'>,
    (<class 'twisted.internet.protocol.BaseProtocol'>,))]]]
>>> dir(Protocol)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', 
 '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', 
 '__hash__', '__implemented__', '__init__', '__le__', '__lt__', 
 '__module__', '__ne__', '__new__', '__providedBy__', '__provides__', 
 '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', 
 '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 
 'connected', 'connectionLost', 'connectionMade', 'dataReceived', 
 'logPrefix', 'makeConnection', 'transport']

Итак, у Protocol есть объект/метод transport, что насчет BaseProtocol:

>>> dir(BaseProtocol)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__',           
 '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', 
 '__hash__', '__implemented__', '__init__', '__le__', '__lt__', 
 '__module__', '__ne__', '__new__', '__providedBy__', '__provides__', 
 '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', 
 '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 
 'connected', 'connectionMade', 'makeConnection', 'transport']
>>> type(BaseProtocol.transport)
<class 'NoneType'>

Почему это None?

Итак, давайте посмотрим на BaseProtocol здесь:

def makeConnection(self, transport): (источник) переопределен в

twisted.internet.endpoints._WrapIProtocol,
twisted.protocols.amp.BinaryBoxProtocol,
twisted.protocols.basic.NetstringReceiver,
twisted.protocols.ftp.ProtocolWrapper,
twisted.protocols.ftp.SenderProtocol,
twisted.protocols.policies.ProtocolWrapper,
twisted.protocols.stateful.StatefulProtocol Make a connection to a
transport and a server.

Примечание:

This sets the 'transport' attribute of this
Protocol, and calls the connectionMade() callback.

Поэтому, когда вызывается makeConnection, он устанавливает атрибут transport протокола.

Так как это работает с фабрикой?

Давайте посмотрим на Factory здесь и < href="https://github.com/twisted/twisted/blob/trunk/src/twisted/internet/protocol.py#L120" rel="noreferrer">источник для buildProtocol

def buildProtocol(self, addr):
    """
    Create an instance of a subclass of Protocol.

    The returned instance will handle input on an incoming server
    connection, and an attribute "factory" pointing to the creating
    factory.

    Alternatively, C{None} may be returned to immediately close the
    new connection.

    Override this method to alter how Protocol instances get created.

    @param addr: an object implementing L{twisted.internet.interfaces.IAddress}
    """
    p = self.protocol()
    p.factory = self
    return p

Хорошо, итак:

class BaseProtocol:
    """
    This is the abstract superclass of all protocols.

    Some methods have helpful default implementations here so that they can
    easily be shared, but otherwise the direct subclasses of this class are more
    interesting, L{Protocol} and L{ProcessProtocol}.
    """
    connected = 0
    transport = None

    def makeConnection(self, transport):
        """Make a connection to a transport and a server.

        This sets the 'transport' attribute of this Protocol, and calls the
        connectionMade() callback.
        """
        self.connected = 1
        self.transport = transport
        self.connectionMade()

Итак, транспорт определен здесь как None, но все же откуда берется transport?

Он исходит из reactor, когда вызывается метод reactor.connect.

Давайте посмотрим на пример TCP:

from twisted.internet import reactor
# 
#
#
reactor.connectTCP('localhost', 80, SomeProtocolFactory())

Из reactor мы вызываем connectTCP как этот:

from twisted.internet.iocpreactor import tcp, udp
#
#
#
def connectTCP(self, host, port, factory, timeout=30, bindAddress=None):
    """
    @see: twisted.internet.interfaces.IReactorTCP.connectTCP
    """
    c = tcp.Connector(host, port, factory, timeout, bindAddress, self)
    c.connect()
    return c

Который вызывает tcp.Connector как from twisted.internet.iocpreactor import tcp, udp здесь:

def connect(self):
    """Start connection to remote server."""
    if self.state != "disconnected":
        raise RuntimeError("can't connect in this state")

    self.state = "connecting"
    if not self.factoryStarted:
        self.factory.doStart()
        self.factoryStarted = 1
    ##################
    # ah here we are
    ##################
    self.transport = transport = self._makeTransport()
    if self.timeout is not None:
        self.timeoutID = self.reactor.callLater(self.timeout, transport.failIfNotConnected, error.TimeoutError())
    self.factory.startedConnecting(self)

Который возвращает транспорт, например этот:

class Connector(TCPConnector):
    def _makeTransport(self):
        return Client(self.host, self.port, self.bindAddress, self,
                      self.reactor)

Что, в свою очередь, создает соединение сокета:

Итак, краткий ответ на ваш вопрос:

Ожидается ли, что отправка сообщений будет реализована независимо от интерфейса протокола?

Protocol инициализирует transport значением None, когда реактор вызывает connect, он устанавливает transport в экземпляре Protocol.

Затем реактор использует транспортный объект протоколов для чтения/записи при установлении входящих/исходящих соединений.

Мы можем отправлять данные через сокет tcp с экземпляром Protocol, используя self.transport.write().

Видеть:

person jmunsch    schedule 08.08.2015
comment
Это удивительный ответ - большое спасибо, что нашли время, чтобы написать его! - person Jonathan; 13.05.2020