[Twisted-Python] How to send a UDP datagram

Can somebody please show me the Twisted way to send a simple UDP datagram? From the examples, I see how to transport.write in response to receiving a datagram or in response to establishing a UDP "connection". But in my application, I'd like to send datagrams blindly, say, to initiate a heartbeat message or to stream audio samples without acknowledgement via an unconnected UDP socket. I'd also like to do this from a function that is not wrapped inside a Twisted protocol class as such, unless that is contrary to the Twisted approach. I am using Twisted version 2.0.1/Python 2.4 on a Linux box. Thank you.

On Sat, 24 Sep 2005 11:33:50 -0400, Drake Smith <drakesmith@adelphia.net> wrote:
When a DatagramProtocol is hooked up to a transport, its startProtocol method is invoked. Here's how I'd write a heartbeat thingy: from twisted.internet import protocol, reactor, task class Heartbeat(protocol.DatagramProtocol): def sendHeartbeat(self): self.transport.write('poingo') def startProtocol(self): self._call = task.LoopingCall(self.sendHeartbeat) self._loop = self._call.start(15) def stopProtocol(self): self._call.stop() reactor.listenUDP(0, Heartbeat()) reactor.run() There's nothing particularly unique to Twisted if you want to turn this inside out and have some other code controlling the loop, but here's an example of that just for completeness: from twisted.internet import protocol, reactor, task class Heartbeat(protocol.DatagramProtocol): def __init__(self, onStart): self.onStart = onStart def startProtocol(self): self.onStart.callback(self) def sendHeartbeat(self): self.transport.write('poingo') class HeartbeatSenderGuy(object): def start(self): d = defer.Deferred() d.addCallback(self._listening) self._port = reactor.listenUDP(0, Heartbeat(d)) def _listening(self, proto): self._proto = proto self._call = task.LoopingCall(self._proto.sendHeartbeat) self._call.start(15) def stop(self): self._call.stop() self._port.stopListening() hb = HeartbeatSenderGuy() hb.start() reactor.run() Hope this helps, Jp

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: 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) 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)) 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. Jp: I see your name a lot within the Python community. Thanks for all your attentiveness to us new comers.

On Sat, 24 Sep 2005 18:28:54 -0400, Drake Smith <drakesmith@adelphia.net> wrote:
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.
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.
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))
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

On Sat, 24 Sep 2005 11:33:50 -0400, Drake Smith <drakesmith@adelphia.net> wrote:
When a DatagramProtocol is hooked up to a transport, its startProtocol method is invoked. Here's how I'd write a heartbeat thingy: from twisted.internet import protocol, reactor, task class Heartbeat(protocol.DatagramProtocol): def sendHeartbeat(self): self.transport.write('poingo') def startProtocol(self): self._call = task.LoopingCall(self.sendHeartbeat) self._loop = self._call.start(15) def stopProtocol(self): self._call.stop() reactor.listenUDP(0, Heartbeat()) reactor.run() There's nothing particularly unique to Twisted if you want to turn this inside out and have some other code controlling the loop, but here's an example of that just for completeness: from twisted.internet import protocol, reactor, task class Heartbeat(protocol.DatagramProtocol): def __init__(self, onStart): self.onStart = onStart def startProtocol(self): self.onStart.callback(self) def sendHeartbeat(self): self.transport.write('poingo') class HeartbeatSenderGuy(object): def start(self): d = defer.Deferred() d.addCallback(self._listening) self._port = reactor.listenUDP(0, Heartbeat(d)) def _listening(self, proto): self._proto = proto self._call = task.LoopingCall(self._proto.sendHeartbeat) self._call.start(15) def stop(self): self._call.stop() self._port.stopListening() hb = HeartbeatSenderGuy() hb.start() reactor.run() Hope this helps, Jp

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: 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) 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)) 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. Jp: I see your name a lot within the Python community. Thanks for all your attentiveness to us new comers.

On Sat, 24 Sep 2005 18:28:54 -0400, Drake Smith <drakesmith@adelphia.net> wrote:
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.
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.
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))
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
participants (2)
-
Drake Smith
-
Jp Calderone