Overview
Unlike TCP, UDP has no notion of connections. A UDP socket can receive datagrams from any server on the network, and send datagrams to any host on the network. In addition, datagrams may arrive in any order, never arrive at all, or be duplicated in transit.
Since there are no multiple connections, we only use a single object,
a protocol, for each UDP socket. We then use the reactor to connect
this protocol to a UDP transport, using the
twisted.internet.interfaces.IReactorUDP
reactor API.
DatagramProtocol
At the base, the place where you actually implement the protocol
parsing and handling, is the DatagramProtocol class. This class will
usually be decended from twisted.internet.protocol.DatagramProtocol
. Most
protocol handlers inherit either from this class or from one of its
convenience children. The DatagramProtocol class receives datagrams, and
can send them out over the network. Received datagrams include the
address they were sent from, and when sending datagrams the address to
send to must be specified.
Here is a simple example:
from twisted.internet.protocol import DatagramProtocol from twisted.internet import reactor class Echo(DatagramProtocol): def datagramReceived(self, data, (host, port)): print "received %r from %s:%d" % (data, host, port) self.transport.write(data, (host, port)) reactor.listenUDP(9999, Echo()) reactor.run()
As you can see, the protocol is registed with the reactor. This means
it may be persisted if it's added to an application, and thus it has
twisted.internet.protocol.DatagramProtocol.startProtocol
and twisted.internet.protocol.DatagramProtocol.stopProtocol
methods that will get called when the protocol is connected and
disconnected from a UDP socket.
The protocol's transport
attribute will
implement the twisted.internet.interfaces.IUDPTransport
interface.
Notice that the host
argument should be an
IP, not a hostname. If you only have the hostname use reactor.resolve()
to resolve the address (see twisted.internet.interfaces.IReactorCore.resolve
).
Connected UDP
A connected UDP socket is slighly different from a standard one - it can only send and receive datagrams to/from a single address, but this does not in any way imply a connection. Datagrams may still arrive in any order, and the port on the other side may have no one listening. The benefit of the connected UDP socket is that it it may provide notification of undelivered packages. This depends on many factors, almost all of which are out of the control of the application, but it still presents certain benefits which occassionally make it useful.
Unlike a regular UDP protocol, we do not need to specify where to send datagrams to, and are not told where they came from since they can only come from address the socket is 'connected' to.
from twisted.internet.protocol import DatagramProtocol from twisted.internet import reactor class Helloer(DatagramProtocol): def startProtocol(self): self.transport.connect("192.168.1.1", 1234) print "we can only send to %s now" % str((host, port)) self.transport.write("hello") # no need for address def datagramReceived(self, data, (host, port)): print "received %r from %s:%d" % (data, host, port) # Possibly invoked if there is no server listening on the # address to which we are sending. def connectionRefused(self): print "Noone listening" # 0 means any port, we don't care in this case reactor.listenUDP(0, Helloer()) reactor.run()
Note that connect()
, like write()
will only accept IP addresses, not
unresolved domain names. To obtain the IP of a domain name use reactor.resolve()
, e.g.:
from twisted.internet import reactor def gotIP(ip): print "IP of 'example.com' is", ip reactor.resolve('example.com').addCallback(gotIP)
Connecting to a new address after a previous connection, or making a connected port unconnected are not currently supported, but will likely be supported in the future.