Как работают 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):

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 здесь и источник 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 Нет, когда реактор вызывает connect это устанавливает transport на Protocol пример.

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

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

Увидеть:

Другие вопросы по тегам