Cm:
Factories create protocol instances.
This means that the factory will use the protocol to figure out how it should listen and send data (see here and also note: you can also write your own protocol).
These are the methods available for 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.
Inherited from BaseProtocol:
Method makeConnection Make a connection to a transport and a server. Method connectionMade Called when a
.
And once the connection was made, we could do something like write data to 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")
But wait, where will Protocol get self.transport from?
>>> 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']
So, Protocol has a transport object / method, what about 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'>
Is that why he is None ?
So, consider BaseProtocol here :
def makeConnection (self, transport): (source), overridden in
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.
Note:
This sets the 'transport' attribute of this Protocol, and calls the connectionMade() callback.
Therefore, when makeConnection is makeConnection , it sets the transport attribute of the protocol.
So how does this work with a factory?
Take a look at Factory here and source for 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
So good:
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()
So transport is defined as None here, but still, where does transport come from?
Its coming from reactor when the reactor.connect method is reactor.connect .
Let's look at a TCP example:
from twisted.internet import reactor
From the reactor call connectTCP as this :
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
What causes tcp.Connector as from twisted.internet.iocpreactor import tcp, udp here :
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
What the transport returns, for example this :
class Connector(TCPConnector): def _makeTransport(self): return Client(self.host, self.port, self.bindAddress, self, self.reactor)
Which in turn creates a socket connection:
So, a short answer to your question:
Are messages expected to be sent independently of the protocol interface?
Protocol initializes the transport value to None, when the reactor calls connect , it sets transport to the Protocol instance.
The reactor then uses the protocol transport object to read / write when making inbound / outbound connections.
We can send data through the tcp socket with the Protocol instance using self.transport.write() .
Cm: