Factory instance not creating a new deferred

82 views Asked by At

I am pretty new to Twisted, so I am sure this is a rookie mistake. I have built a simple server which receives a message from the client and upon receipt of message the server fires a callback which prints the message to the console.

At first instance, the server works as expected. Unfortunately, when I start up a second client I get the follow error "twisted.internet.defer.AlreadyCalledError." It was my understanding that the factory would make a new instance of the deferred i.e. the new deferred wouldn't have been called before?

Please see the code below. Any help would be very appreciated.

import sys
from twisted.internet.protocol import ServerFactory, Protocol
from twisted.internet import defer

class LockProtocol(Protocol):

  lockData = ''

  def dataReceived(self, data):
    self.lockData += data

    if self.lockData.endswith('??'):
      self.lockDataReceived(self.lockData)

  def lockDataReceived(self, lockData):
    self.factory.lockDataFinished(lockData)

class LockServerFactory(ServerFactory):

  protocol = LockProtocol  

  def __init__(self):
    self.deferred = defer.Deferred() # Initialise deferred

  def lockDataFinished(self, lockData):
      self.deferred.callback(lockData)

  def clientConnectionFailed(self, connector, reason):
      self.deferred.errback(reason)


def main():

  HOST = '127.0.0.1' # localhost
  PORT = 10001

  def got_lockData(lockData):
    print "We have received lockData. It is as follows:", lockData

  def lockData_failed(err):
    print >> sys.stderr, 'The lockData download failed.'
    errors.append(err)


  factory = LockServerFactory()

  from twisted.internet import reactor

  #  Listen for TCP connections on a port, and use our factory to make a protocol instance for each new connection

  port = reactor.listenTCP(PORT,factory)

  print 'Serving on %s' %  port.getHost()

  # Set up callbacks

  factory.deferred.addCallbacks(got_lockData,lockData_failed)

  reactor.run() # Start the reactor

if __name__ == '__main__':
    main()
1

There are 1 answers

0
Jean-Paul Calderone On BEST ANSWER

Notice that there is only one LockServerFactory ever created in your program:

factory = LockServerFactory()

However, as many LockProtocol instances are created as connections are accepted. If you have per-connection state, the place to put it is on LockProtocol.

It looks like your "lock data completed" event is not a one-off so a Deferred is probably not the right abstraction for this job.

Instead of a LockServerFactory with a Deferred that fires when that event happens, perhaps you want a multi-use event handler, perhaps custom built:

class LockServerFactory(ServerFactory):

  protocol = LockProtocol  

  def __init__(self, lockDataFinished):
    self.lockDataFinished = lockDataFinished

 factory = LockServerFactory(got_lockData)

(Incidentally, notice that I've dropped clientConnectionFailed from this implementation: that's a method of ClientFactory. It will never be called on a server factory.)