[Twisted-Python] How to send a UDP datagram

Jp Calderone exarkun at divmod.com
Sat Sep 24 17:33:11 MDT 2005


On Sat, 24 Sep 2005 18:28:54 -0400, Drake Smith <drakesmith at adelphia.net> wrote:
>
> [snip]
>
>Jp,
>
>Thank you for the extra effort to show us OO novices how to control the loop 
>from outside the protocol class. I've read several inquiries to this effect 
>but nobody has ever explained it.
>
>I generalized your example to give me what I want: the ability to invoke UDP 
>datagram messages from outside the protocol class:

For the most part, your code looks good.  The one thing it gets somewhat incorrect is that it does not wait for the protocol to become connected before attempting to use its transport.

>
>from twisted.internet import protocol, reactor, defer
>
>class UDPsender(protocol.DatagramProtocol):
>     def __init__(self, onStart):
>         self.onStart = onStart
>
>     def startProtocol(self):
>         self.onStart.callback(self)

  Only after startProtocol has been called is `self.transport' bound to something meaningful.  That's the reason for the Deferred here - so you know when you can start using the protocol instance.  Until this Deferred fires, there's no connection to write bytes too.

  With SelectReactor (and perhaps all the other currently implemented reactors), startProtocol may get called synchronously as a result of listenUDP (that is, before listenUDP returns) - but this is not guaranteed, so it's best not to rely on it.

>
>     def sendMsg(self, data, (host, port)):
>         self.transport.write(data, (host, port))
>
>class DatagramSender(object):
>     def start(self):
>         d = defer.Deferred()
>         d.addCallback(self._listening)
>         self._port = reactor.listenUDP(0, UDPsender(d))
>
>     def _listening(self, proto):
>         global myProto
>         myProto = proto
>
>     def sendMsg(self, data, (host, port)):
>         global myProto
>         myProto.sendMsg(data, (host, port))
>
>     def stop(self):
>         self._call.stop()
>         self._port.stopListening()
>
>ds = DatagramSender()
>ds.start()
>ds.sendMsg("hello port 20006", ("127.0.0.1", 20006))
>ds.sendMsg("hello port 20007", ("127.0.0.1", 20007))

  The above two lines are the ones that might explode if listenUDP doesn't give its protocol a transport synchronously.  To get them to execute at the correct time, you might try adding "return d" to the end of the definition of start, and then changing the above to:

    ds = DatagramSender()
    d = ds.start()
    def startSendingStuff(ignored):
        ds.sendMsg("hello port 20006", ("127.0.0.1", 20006))
        ds.sendMsg("hello port 20007", ("127.0.0.1", 20007))

>reactor.run()
>
>I tried a simpler implementation.....
>
>from twisted.internet import protocol, reactor
>
>class UDPsender(protocol.DatagramProtocol):
>
>     def sendMsg(self, data, (host, port)):
>         self.transport.write(data, (host, port))
>
>ds = UDPsender()
>ds.sendMsg("hello port 20006", ("127.0.0.1", 20006))
>ds.sendMsg("hello port 20007", ("127.0.0.1", 20007))
>reactor.run()
>
>.....but I get the infamous "AttributeError: 'NoneType' object has no 
>attribute 'write'" error. I'll stay with the former version. It's not 
>exactly as compact as "mySocket.sendto(data, addr)" but I know it will cause 
>me less headaches as my program evolves.

If you just add in a "reactor.listenUDP(0, ds)" before the call to sendMsg, the above should work, though the same caveat about synchronous listenUDP/startProtocol interaction applies.

If you prefer, you can fold the `start' method (and supporting methods) into UDPSender - I only created two separate classes to demonstrate that the functionality could in fact be separated.

>
>Jp: I see your name a lot within the Python community. Thanks for all your 
>attentiveness to us new comers.

Glad to be of help :)

Jp




More information about the Twisted-Python mailing list