Search code examples
pythonsocketstwisted

Set a timeout for an UDP socket with Twisted (python)


I'm using Twisted in a python program. I send data every minute and receive data on a UDP socket and I need to set a timeout.

I found a way to do this, but I would like to know if it's a good way to do that.

This is the idea of my code :

class UDPSocket(DatagramProtocol):
   def startProtocol(self):
       self.transport.connect(self.HOST, self.PORT)
       reactor.callLater(1, self.send_data)

   def send_data(self):
       reactor.callLater(60, self.send_data)
       self.transport.write("Some data")
       self.timeoutCallId = reactor.callLater(5, raise_timeout)

   def datagramReceived(self, data, (host, port)):
       if self.timeoutCallId.active():
           self.timeoutCallId.cancel()
       #Do something with data

   def raise_timeout(self):
       #Do something to manage timeout

reactor.listenUDP(port, UDPSocket())
reactor.run()

Thanks for your advices

EDIT :

It works but I have a problem.

If a timeout occured, I'm going into the raise_timeout function correctly, but when the next "send_data()" is called, the self.transport.write("data") function raise an exception : 'NoneType' object has no attribute 'write'.

I guess self.transport is disconnected.

How can I fix that ?


Solution

  • I found a solution.

    When we use UDP, when we send a datagram, if no answer is received after less than 1 second, "self.transport" is set to None and "stopProtocol" method is called. Once we leave "stopProtocol" method, reactor stops to listen the specified UDP port.

    So this is my solution code :

    class UDPSocket(DatagramProtocol):
        def __init__(self, mainreactor):
            self._reactor = mainreactor
    
        def startListenUDP(self):
            try :
                self._reactor.listenUDP(KeepAlive.PORT, self)
            except Exception, e :
                pass
    
        def startProtocol(self) :
            self.transport.connect(self.host, self.port)
    
        def stopProtocol(self):
            print "Transport disconnected !"
            self._reactor.callLater(0.1, self.startListenUDP)
    
        def raise_timeout(self) :
            print "Timeout !"
    
        def datagramReceived(self, datagram, host):
            try :
                if self.timeout_id.active():
                    self.timeout_id.cancel()
            except Exception, e :
                pass
            print datagram
    
        def sendDatagram(self):
            datagram = "Some Data"
            try :
                self.transport.write(datagram)
                self.timeout_id = self._reactor.callLater(TIMEOUT, 
                    self.raise_timeout)
            except Exception, e :
                self._reactor.callLater(0.1, self.startListenUDP)
    
    def main():
        protocol = UDPSocket(reactor)
        t = reactor.listenUDP(PORT, protocol)
        l = task.LoopingCall(protocol.sendDatagram)
        l.start(60)
        reactor.run()
    
    #MAIN
    if __name__ == '__main__':
        main()