Thursday, June 7, 2018

QLocalSocket under *nix is SOCKET_STREAM only

While developing a library for Qt to interact with Sense HAT add-on board for Raspberry Pi, I started with the software side, the Sense Emulator. This application emulates the components of the board, and makes the data from emulated sensors, stick and LED matrix available through several files. Under *nix the stick events are provided via a Unix domain local socket as datagrams, constructed in Python like this:

(socket.AF_UNIX, socket.SOCK_DGRAM, '/dev/shm/rpi-sense-emu-stick')

So I thought QLocalSocket was the perfect fit for connecting the library to the Sense Emulator server socket, receive and decode the data and publish it as signals to Qt applications using the library.
But as I started making some tests, I was not able to receive any data. And although everything was in place, it was not working.



I haven't worked with Unix domain sockets before, the only thing I knew was that instead of an address and port, this kind of socket uses a file path as the endpoint. Looking at the Python example provided by Sense Emulator I discovered that besides the file created by the emulator GUI, another file was created whenever I tried the client (interestingly having the pid of the client as part of the file path).

But QLocalSocket didn't create another file, and the its API doesn't provide a way to set or declare one. The only path it can be set is the destination you want to connect to, the server endpoint. I was trapped.

As decided as I was to make this work, I started looking for some basic C client example and some documentation to learn about Unix domain sockets using datagrams (SOCKET_DGRAM). I finally found what I needed to know: when using unix datagram sockets the client has to bind() to its own endpoint, then connect() to the server's endpoint.

Different from stream sockets (TCP or Unix domain), datagram sockets need endpoints defined for both the server AND the client. When one establishes a connection in stream sockets, an endpoint for the client is implicitly created by the operating system. Whether this corresponds to an ephemeral TCP/UDP port, or a temporary inode for the Unix domain, the endpoint for the client is created for you. That is why you don't normally need to issue a call to bind() for stream sockets in the client.

Armed with this information, I found a couple of C examples that finally worked. But why QLocalSocket was not working? The answer came from some posts and the QLocalSocket source code itself: it only uses SOCKET_STREAM:


qt_safe_socket(PF_UNIX, SOCK_STREAM, 0, O_NONBLOCK))

and there's no way to change it to SOCKET_DGRAM.

No comments:

Post a Comment