Reputation: 109
CLIENT:
#!/usr/bin/env python
from twisted.internet import reactor, protocol
class EchoClient(protocol.Protocol):
def __init__(self, arg):
self.arg = arg
def connectionMade(self):
self.transport.write("hello, world!")
def dataReceived(self, data):
print "Server said:", data
self.transport.loseConnection()
def connectionLost(self, reason):
print "connection lost"
class EchoFactory(protocol.ClientFactory):
protocol = EchoClient
def buildProtocol(self, address):
proto = protocol.ClientFactory.buildProtocol(self, address, 12)
self.connectedProtocol = proto
return proto
def clientConnectionFailed(self, connector, reason):
print "Connection failed - goodbye!"
reactor.stop()
def clientConnectionLost(self, connector, reason):
print "Connection lost - goodbye!"
reactor.stop()
def main():
f = EchoFactory()
reactor.connectTCP("localhost", 8000, f)
reactor.run()
if __name__ == '__main__':
main()
SERVER:
#!/usr/bin/env python
from twisted.internet import reactor, protocol
from twisted.application import service, internet
class Echo(protocol.Protocol):
def dataReceived(self, data):
self.transport.write(data)
def main():
factory = protocol.ServerFactory()
factory.protocol = Echo
reactor.listenTCP(8000,factory)
reactor.run()
if __name__ == '__main__':
main()
ERROR:
exceptions.TypeError: buildProtocol() takes exactly 2 arguments (3 given)
QUESTION:
How can I get the EchoClient
class in the CLIENT
to accept parameters and assign instance variables ( such as arg in the EchoClient
constructor above)? As noted below, it was previously suggested that I override the buildProtocol
function, but my attempt at doing so has lead me to the above error. I am not really sure where to go from here. I suppose my question can be generalize to: how can I add instance variables to a protocol?
Upvotes: 2
Views: 2851
Reputation: 156158
you wrote:
def buildProtocol(self, address):
proto = protocol.ClientFactory.buildProtocol(self, address, 12)
that is, you are overriding ClientFactory.buildProtocol
and calling the parent class with a different signature than it knows how to handle.
Passing data from the factory to the client is only a little tricky. You can provide any __init__
you want to the factory, but twisted creates instances of IProtocol
itself. Fortunately, most factories assign themselves to the factory
attribute of the protocol, once it's ready to go:
class MyClientProtocol(protocol.Protocol):
def connectionMade(self):
# use self.factory here:
self.transport.write(self.factory.arg)
class MyClientFactory(protocol.ClientFactory):
protocol = MyClientProtocol
def __init__(self, arg):
self.arg = arg
In fact, the whole ProtocolFactory business is to support this kind of use; but be mindful; many instances of Protocol
will share a single instance of their factory; use the factory for configuration but manage state in the protocol.
It's certainly possible that the way the standard family of Protocol/Factory implementations don't suit your needs, and that's also reasonable, so long as you fully implement the IProtocol
and IProtocolFactory
interfaces. The base classes exist because they handle most of the cases for you, not because they are the only possible implementation.
Upvotes: 7
Reputation: 13616
It's not clear from your question what exactly your tryed and what exactly the error was, but anyway you have to do two steps:
EchoClient
's constructor take whatever arguments you need it to take and initialise whatever field you need it to initialise.buildProtocol
method in your factory to supply those arguments to your protocol.Upvotes: 1