Skip to content

litesync/libuv_message_framing

Repository files navigation

libuv_message_framing

Build Status

Message-based communication for libuv

This code implements length-prefixed message framing on top of streams.

It works with TCP, Unix domain sockets (Linux) and Named Pipes (Windows).

Usage

Stream Initialization for TCP

uv_msg_t* socket = malloc(sizeof(uv_msg_t));
uv_msg_init(loop, socket, UV_TCP);

Stream Initialization for Unix domain sockets or Named Pipes

uv_msg_t* socket = malloc(sizeof(uv_msg_t));
uv_msg_init(loop, socket, UV_NAMED_PIPE);

Sending Messages

uv_msg_send((uv_msg_write_t*)req, (uv_msg_t*) socket, msg, size, write_cb);

Receiving Messages

uv_msg_read_start((uv_msg_t*) socket, alloc_cb, msg_read_cb, free_cb);

Examples

By default libuv does not handle memory management for requests. The above functions were implemented using the same principle so you are free to use the memory management you want with them.

But for ease of use and understanding we included 2 examples that use the system malloc/free functions.

The message reading implementation is the same.

Both examples also have a user function called send_message, but implemented in a different way.

Example 1

The example.c has a basic send_message function that only accepts dynamically allocated messages. The callback must always be supplied and it must release the memory used for the message and for the request.

send_message(socket, msg, size, on_msg_sent);

Example 2

The example2.c has a more elaborated send_message function that accepts both static, transient and dynamic messages. This concept is inherited from SQLite.

send_message(socket, msg, size, free_fn, on_msg_sent, user_data);

The free_fn argument can accept these 3 values:

  • UV_MSG_STATIC

    Use when the message pointer you pass to the function will be valid until after the message is sent.

  • UV_MSG_TRANSIENT

    Use when the message memory will be discarded soon, probably before the message is sent. The function will make a copy of the message. Remember that the message sending is asynchronous.

  • A pointer to the destructor or free function that must be automatically called to release the memory associated with the message upon the complete delivery or failure.

The callback and user data arguments are optional. Examples:

Sending a static message with no callback:

msg = "Hello Mom!";
send_message(socket, msg, strlen(msg)+1, UV_MSG_STATIC, 0, 0);

Sending a dynamically allocated message with a notification callback function:

msg = strdup("Hello Dad!");
send_message(socket, msg, strlen(msg)+1, free, on_msg_sent, extra_data);

Compiling

On Linux

Using TCP:

gcc echo-server.c -o echo-server -luv
gcc example.c -o example -luv
gcc example2.c -o example2 -luv

Using unix domain sockets:

gcc echo-server.c -o echo-server -luv -DUSE_PIPE_EXAMPLE
gcc example.c -o example -luv -DUSE_PIPE_EXAMPLE
gcc example2.c -o example2 -luv -DUSE_PIPE_EXAMPLE

On Windows

Using TCP:

gcc echo-server.c -o echo-server -llibuv -lws2_32
gcc example.c -o example -llibuv -lws2_32
gcc example2.c -o example2 -llibuv -lws2_32

Using named pipes:

gcc echo-server.c -o echo-server -llibuv -lws2_32 -DUSE_PIPE_EXAMPLE
gcc example.c -o example -llibuv -lws2_32 -DUSE_PIPE_EXAMPLE
gcc example2.c -o example2 -llibuv -lws2_32 -DUSE_PIPE_EXAMPLE

Testing

On Linux

cd test
gcc test.c -o test -luv
LD_LIBRARY_PATH=/usr/local/lib ./test

# or with valgrind:
LD_LIBRARY_PATH=/usr/local/lib valgrind --leak-check=full --show-reachable=yes ./test

On Windows

cd test
gcc test.c -o test -llibuv -lws2_32
test

Compatibility

This code is compatible with implementations in other languages that encode the length in big endian.

Some examples:

TO DO

  • (maybe) use uv_buf_t bufs[] instead of void *msg on uv_msg_send()

Releases

No releases published

Packages

No packages published

Languages