libuv

Language: C

Networking / Async

libuv was originally developed as part of Node.js to provide a consistent, event-driven, asynchronous I/O layer across different platforms. It is now used in many other projects that require efficient non-blocking I/O, including networking applications, servers, and frameworks.

libuv is a multi-platform C library that provides asynchronous I/O, event loops, timers, and cross-platform abstractions for networking, filesystems, and concurrency. It is designed to support high-performance, scalable applications.

Installation

linux: sudo apt install libuv1-dev
mac: brew install libuv
windows: Download precompiled binaries from https://libuv.org/ or build from source

Usage

libuv allows developers to handle TCP/UDP sockets, files, and timers asynchronously using an event loop. It supports callbacks, handles, streams, and integrates well with C/C++ applications requiring high concurrency.

Initializing the event loop

#include <uv.h>
#include <stdio.h>

int main() {
    uv_loop_t *loop = uv_default_loop();
    printf("Loop started\n");
    uv_run(loop, UV_RUN_DEFAULT);
    uv_loop_close(loop);
    return 0;
}

Initializes the default libuv event loop, runs it (though no events are scheduled), and closes it.

Using a timer

#include <uv.h>
#include <stdio.h>

void timer_cb(uv_timer_t* handle) {
    printf("Timer fired!\n");
}

int main() {
    uv_loop_t *loop = uv_default_loop();
    uv_timer_t timer_req;
    uv_timer_init(loop, &timer_req);
    uv_timer_start(&timer_req, timer_cb, 1000, 0); // Fire once after 1000ms
    uv_run(loop, UV_RUN_DEFAULT);
    uv_loop_close(loop);
    return 0;
}

Sets up a one-time timer that calls a callback after 1 second.

TCP server

#include <uv.h>
#include <stdio.h>

void on_new_connection(uv_stream_t *server, int status) {
    if (status < 0) {
        fprintf(stderr, "New connection error: %s\n", uv_strerror(status));
        return;
    }
    printf("New client connected\n");
}

int main() {
    uv_loop_t *loop = uv_default_loop();
    uv_tcp_t server;
    uv_tcp_init(loop, &server);
    struct sockaddr_in addr;
    uv_ip4_addr("0.0.0.0", 7000, &addr);
    uv_tcp_bind(&server, (const struct sockaddr*)&addr, 0);
    int r = uv_listen((uv_stream_t*)&server, 128, on_new_connection);
    if (r) {
        fprintf(stderr, "Listen error: %s\n", uv_strerror(r));
        return 1;
    }
    uv_run(loop, UV_RUN_DEFAULT);
    uv_loop_close(loop);
    return 0;
}

Creates a basic TCP server that listens on port 7000 and prints a message when a client connects.

Asynchronous file read

#include <uv.h>
#include <stdio.h>
#include <stdlib.h>

void read_cb(uv_fs_t* req) {
    printf("File content: %s\n", (char*)req->bufs->base);
    uv_fs_req_cleanup(req);
}

int main() {
    uv_loop_t *loop = uv_default_loop();
    uv_fs_t open_req;
    uv_fs_open(loop, &open_req, "test.txt", O_RDONLY, 0, NULL);
    uv_fs_t read_req;
    char *buffer = malloc(1024);
    uv_buf_t iov = uv_buf_init(buffer, 1024);
    uv_fs_read(loop, &read_req, open_req.result, &iov, 1, 0, read_cb);
    uv_run(loop, UV_RUN_DEFAULT);
    uv_loop_close(loop);
    free(buffer);
    return 0;
}

Demonstrates asynchronous file reading using libuv.

UDP server

#include <uv.h>
#include <stdio.h>

void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) {
    buf->base = malloc(suggested_size);
    buf->len = suggested_size;
}

void on_read(uv_udp_t *req, ssize_t nread, const uv_buf_t *buf, const struct sockaddr *addr, unsigned flags) {
    if (nread > 0) printf("Received: %s\n", buf->base);
    free(buf->base);
}

int main() {
    uv_loop_t *loop = uv_default_loop();
    uv_udp_t recv_socket;
    uv_udp_init(loop, &recv_socket);
    struct sockaddr_in addr;
    uv_ip4_addr("0.0.0.0", 7000, &addr);
    uv_udp_bind(&recv_socket, (const struct sockaddr*)&addr, 0);
    uv_udp_recv_start(&recv_socket, alloc_buffer, on_read);
    uv_run(loop, UV_RUN_DEFAULT);
    uv_loop_close(loop);
    return 0;
}

Sets up a UDP server that asynchronously receives messages.

Error Handling

UV_EADDRINUSE: Occurs when trying to bind to an address/port already in use. Choose a free port or close conflicting applications.
UV_ENOENT: File or directory not found. Verify paths for filesystem operations.
UV_ECONNREFUSED: Connection refused on TCP/UDP. Check that the server is running and reachable.

Best Practices

Always initialize and close the event loop properly.

Free memory for buffers and handles to avoid leaks.

Use callbacks to handle events asynchronously.

Leverage libuv timers and async handles for concurrency instead of threads.

Handle errors in callbacks to prevent crashing the loop.