[Twisted-Python] Sending other things than strings in UDP packets

Paul Campbell paul at ref.nmedia.net
Wed Oct 6 15:38:55 MDT 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