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).
uv_msg_t* socket = malloc(sizeof(uv_msg_t));
uv_msg_init(loop, socket, UV_TCP);
uv_msg_t* socket = malloc(sizeof(uv_msg_t));
uv_msg_init(loop, socket, UV_NAMED_PIPE);
uv_msg_send((uv_msg_write_t*)req, (uv_msg_t*) socket, msg, size, write_cb);
uv_msg_read_start((uv_msg_t*) socket, alloc_cb, msg_read_cb, free_cb);
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.
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);
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);
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
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
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
cd test
gcc test.c -o test -llibuv -lws2_32
test
This code is compatible with implementations in other languages that encode the length in big endian.
Some examples:
- Node.js frame-stream
- Python struct
- (maybe) use
uv_buf_t bufs[]
instead ofvoid *msg
on uv_msg_send()