[Twisted-Python] Sending other things than strings in UDP packets
Paul Campbell
paul at ref.nmedia.net
Wed Oct 6 17:38:55 EDT 2004
Sending anything via any protocol is fairly easy with python in general.
There are two ways to do it depending on your particular goals.
The "python way" is as follows:
message = pickle.dumps(my structures)
my structures = pickle.loads(message)
Read the documentation on the pickle module for more information. And be
forewarned: pickle will dump/load ANYTHING. For safety reasons, there's also
a "safe_pickle" variant floating around.
The underlying banana protocol is really just another pickle module for use
inside PB. I haven't tried using it separately from PB though.
The other "unpythonese" way of doing it is using the struct module:
message = struct.pack("format", param1, param2, param3)
param1, param2, param3 = struct.unpack("format", message)
The advantage of the struct library is that you have complete and total control
over every byte so you can deal with non-python data.
In reality, I personally use both. pickle is great for providing a totally
unstructured interface to higher level protocols that have no desire in
looking directly at low level packet formats. struct does just the opposite.
For instance, here's a very simple UDP RPC module with no error checking
whatsoever, no safety, no handling of very long packets (or checking for
that case), no timeouts, or anything but a very basic rudimentary idea of
how to assemble an RPC protocol.
class UDPRPC(DatagramProtocol):
REQUEST = 0
RESPONSE = 1
def start(self):
"""Create a dictionary of pending RPC's"""
self.pending = dict()
def call(self, address, remote_class, remote_method, *remote_args, **remote_kargs):
"""The actual RPC call local (stub) interface. Note that
we can even get creative by overloading __calls__ and
similar interfaces to make it look 100% like a real class."""
d = defer.Deferred() # Create a deferred return.
# The following is an index to look up the RPC response later
nonce = random.randomint(0, 2^32-1)
index = struct.pack("!I!I!H", nonce, address[0], address[1])
self.pending[index] = d # Store the deferred for the response to follow
self.sendMessage(self.REQUEST, address,
(remote_class, remote_method, remote_args, remote_kargs))
return d
def sendMessage(self, type, address, nonce, data):
"Do the conversion to a packet and send it."
packet = chr(type)+struct.pack("!I", nonce)+
pickle.dumps(data)
transport.write(packet)
return d
def datagramReceived(self, packet, address):
"""Handle incoming packets."""
type = ord(packet[0])
nonce = struct.unpack("!I", packet[1:4])
message = pickle.loads(packet[5:])
if type == REQUEST: # Handling a remote call
remote_class, remote_method, remote_args, remote_kargs = message
result = remote_class.remote_method(*remote_args, **remote_kargs)
self.sendMessage(self.RESPONSE, address, nonce, result)
else: # Assume that this is the response, so return the deferred
index = struct.pack("!I!I!H", nonce, address[0], address[1])
caller = self.pending[index]
del self.pending[index]
caller.Callback(message)
More information about the Twisted-Python
mailing list