/libpomp

Printf Oriented Message Protocol

Primary LanguageCBSD 3-Clause "New" or "Revised" LicenseBSD-3-Clause

pomp - Printf Oriented Message Protocol

This library offers a simple protocol to encode/decode messages and exchange
them between processes on a socket (inet or local).

A message is composed of a 32-bit id and a payload. The payload is composed
of any number of typed arguments.

The encoding/decoding is done with printf/scanf like functions with a format
string and a variable number of arguments. However, no actual string formatting
is done, the payload is a binary representation of arguments.

The payload contains the type of the arguments as well as the value of the
arguments, hence making it robust during decoding at runtime.

Both sender and receiver shall provide the format string. How the knowledge of
the format string is shared is out of the scope of this library. It can simply
be a shared header with defines.

Key points :
- no need to use a description language and code generator, the format string
  indicates the type of the message arguments.
- the use of printf/scanf like functions allows the compiler to check the
  coherence between the format string and the actual type of the arguments so
  the encoding/decoding engine can be generic and safe.
- as the type of arguments is included in the payload, the decoding engine can
  check that the provided format string matches the actual message content.

How to use :

1) Instantiate a server and a client context object.
   They can be in separate processes or in the same one. Communication is
   done with sockets (inet or local).
   The server will listen for any number of incoming connections on the given
   address. Both server and client are notified of the remote peer connection
   and disconnection as well as the reception of a message.
   The client/server context object offers a file descriptor that need to
   be added in an event loop to process io events.

   ctx = pomp_ctx_new(&cb, NULL);
   pomp_ctx_listen(ctx, addr, addrlen);
     or
   pomp_ctx_connect(ctx, addr, addrlen);
   fd = pomp_ctx_get_fd(ctx);

   The library automatically handles reconnections after timeout and remote
   disconnections.

  Note: retrieving the fd of the context to put it in an external event loop
        is only available under linux. For other systems, please take a look
        at the loop API.

2) Send a message.
   A message can be sent at the context object level or at the connection
   object level. The difference is mainly for server where a broadcast is done
   at the context object level.

   uint32_t counter = 10;
   uint32_t msgid = 42;
   pomp_ctx_send(ctx, msgid, "%d%s", counter, "PING");

   The message id can be used freely to identify the message, mainly its
   format string at reception.
   The format string is a subset of what is supported by a standard printf
   function but allows the compiler to check that given arguments have the
   correct type. Basically only format specifier is supported, no extra string
   shall be given.

3) Receive the message.
   The reception of a message is indicated in the context object callback. You
   just need to get the message id and handle the message accordingly.

   uint32_t counter = 0;
   char *str = NULL;
   uint32_t msgid = pomp_msg_get_id(msg);
   pomp_msg_read(msg, "%d%ms", &counter, &str);
   free(str);

   Again the format string allows the compiler to check that the type of the
   argument is valid. The decoding engine will also make sure it matches the
   real message payload.
   Note that string decoding uses a special string specifier '%ms' that
   indicate that an allocated string will be returned. This avoid buffer
   overflows by ensuring corret size of buffer. Caller needs however to free it
   afterwards. This '%ms' specifier is a GNU extension to scanf but will be
   part of POSIX specifications.

4) Cleanup.
   Simply stop and destroy created objects.
   pomp_ctx_stop(ctx);
   pomp_ctx_destroy(ctx);

Even if the library can be used without any generated code from description
files, it can be used as a basic component to build a more complicated protocol
between 2 entities.

This library should become useful when one wants to exchange messages between
2 processes. The library handles many aspects of an inter-process communication
that can be hard to make it right, simple and robust.

Format string specification:

- %hhi : 8-bit signed integer.
- %hi  : 16-bit signed integer.
- %i   : 32-bit signed integer.
- %li  : 32-bit signed integer if __WORDSIZE is 32 else 64-bit signed integer.
- %lli : 64-bit signed integer.
Note : variants with %d are also supported for signed integer.

- %hhu : 8-bit unsigned integer.
- %hu  : 16-bit unsigned integer.
- %u   : 32-bit unsigned integer.
- %lu  : 32-bit unsigned integer if __WORDSIZE is 32 else 64-bit signed integer.
- %llu : 64-bit unsigned integer.

- %s   : string (encoding only).
- %ms  : string (decoding only). Caller shall free the returned string.
Note : for decoding, %ms expects a pointer to a string (char **) not just a
       string (char*) like a standard scanf with %s.

- %p%u : buffer (with its size). For decoding, buffer is still owned by the
         message, caller shall NOT free the returned pointer.
Note : both %p AND %u shall be given.

- %f   : 32-bit floating point.
- %lf  : 64-bit floating point.
Note : variants with %F, %g, %G, %e, %E are also supported for floating point.

- %x   : file descriptor. Can only be sent on a local unix socket.
Note : for encoding, the file descriptor will be internally duplicated. For
       decoding, the returned file descriptor shall NOT be closed by caller.
       However caller shall duplicate it if it needs to use it after the
       message is released.