user3147642
user3147642

Reputation: 21

Python: reading data from a twisted socket into a SWIG-ed structure

I'm writing a Python client to connect to a server written in C that sends status in a binary structure. I've wrapped the C structure with SWIG, but I need to handle the data returned from the socket as a C structure. In particular, I want to cast the data passed to dataReceived() as a iwrf_ui_task_operations structure.

Do I need to write (and SWIG) a helper function that is passed 'data', and returns a iwrf_ui_task_operations struct?

Here's a simple test program:

from twisted.internet import reactor, protocol
import syscon_interface


class SysconClient(protocol.Protocol):
    """Once connected, receive messages from syscon."""

    def connectionMade(self):
        print "connectionMade"

    def dataReceived(self, data):
        "As soon as any data is received, write it out."
        # this constructor does not accept 'data' as an argument  :-(
        to = syscon_interface.iwrf_ui_task_operations_t()  
        print "Server said:", data

    def connectionLost(self, reason):
        print "connection lost"

class SysconClientFactory(protocol.ClientFactory):
    protocol = SysconClient

    def clientConnectionFailed(self, connector, reason):
        print "Connection failed - goodbye!"
        reactor.stop()

    def clientConnectionLost(self, connector, reason):
        print "Connection lost - goodbye!"
        reactor.stop()


# this connects the protocol to a server running on port 2515
def main():
    f = SysconClientFactory()
    reactor.connectTCP("localhost", 2515, f)
    reactor.run()

# this only runs if the module was *not* imported
if __name__ == '__main__':
    main()       

Upvotes: 2

Views: 202

Answers (1)

Glyph
Glyph

Reputation: 31860

You don't want to do this. The data passed to dataReceived is a TCP segment, not a protocol message. So you might receive some of your structure, or all of it, or more than one, or data that starts in the middle of one.

See this Twisted FAQ.

Also you don't want to do this at all. Different C compilers can totally validly generate different layouts for this structure, and your platform's endianness will get involved, and it's just generally a bad scene.

If you're going to do it though (and based on the framing of your question, I assume you have to), first you need to make sure that your exact toolchain versions (C compiler version, SWIG version, Python version, Python build options, and so on) are all exactly synchronized. Then you need to write a framing protocol, like those in twisted.protocols.basic, which deals with fixed-width records based on sizeof(iwrf_ui_task_operations_t), and then once you've split that up, a wrapper function which takes a char* data and a int length and constructs your struct would be a logical next step.

Finally, don't use SWIG, use CFFI. It's substantially easier to write the bindings, more portable to other runtimes (PyPy, for example, can actually JIT your calls into C), and substantially safer (far less chance of a segfault).

Upvotes: 4

Related Questions