Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

codepr/ev

Repository files navigation

EV

Light event-loop library loosely inspired by the excellent libuv, in a single small (< 700 sloc) header, based on the common IO multiplexing implementations available, epoll on linux, kqueue on BSD-like and OSX, poll/select as a fallback, dependencies-free. A common usage of the library is to craft event-driven TCP servers, ev_tcp.h exposes a set of APIs to fulfill this purpose in a simple manner.

TLS is supported as well through OpenSSL, and source have to be compiled adding a -DHAVE_OPENSSL=1 to enable it. Of course it requires libssl-dev installed on the host machine to work.

In conclusion the library is composed of 2 distinct modules

  • ev.h a generic eventloop for I/O bound concurrency on a single-thread:
    • Based on the best multiplexing IO implementation available on the host, supporting epoll/poll/select on linux and kqueue on BSD
    • All IO operations are done in a non-blocking way
    • Support for time based repeated tasks
  • ev_tcp.h exposes a set of APIs to simply create an event-driven TCP server using ev.h as the main engine:
    • TCP/UNIX socket connections
    • Basic TLS support through OpenSSL
    • Callback oriented design

To adopt these libraries it's required to define a value just before inclusion in one file only in the project:

#define EV_SOURCE
#include "ev.h"

Or in case of ev_tcp.h

#define EV_SOURCE
#define EV_TCP_SOURCE
#include "ev_tcp.h"

Running examples

A simple event-driven echo server

$ make echo-server

Write periodically on the screen ping and pong on different frequencies, referred as cron tasks

$ make ping-pong

Helper APIs

Lightweight event-driven hello world TCP server

#include <stdio.h>
#include <stdlib.h>
#define EV_SOURCE // add before ev_tcp
#define EV_TCP_SOURCE // add before ev_tcp
#include "../ev_tcp.h"
#define HOST "127.0.0.1"
#define PORT 5959
#define BACKLOG 128
static void on_close(ev_tcp_handle *client, int err) {
 (void) client;
 if (err == EV_TCP_SUCCESS)
 printf("Connection closed\n");
 else
 printf("Connection closed: %s\n", ev_tcp_err(err));
 free(client);
}
static void on_write(ev_tcp_handle *client) {
 (void) client;
 printf("Written response\n");
}
static void on_data(ev_tcp_handle *client) {
 printf("Received %li bytes\n", client->buffer.size);
 if (strncmp(client->buffer.buf, "quit", 4) == 0)
 ev_tcp_close_handle(client);
 else
 // Enqueue a write of the buffer content for the next loop cycle
 ev_tcp_queue_write(client);
 // If want to respond on the same loop cycle
 // ev_tcp_write(client);
}
static void on_connection(ev_tcp_handle *server) {
 ev_tcp_handle *client = malloc(sizeof(*client));
 if (!client) {
 fprintf(stderr, "On connection failed: Out of memory");
 exit(EXIT_FAILURE);
 }
 int err = ev_tcp_server_accept(server, client, on_data, on_write);
 if (err < 0)
 free(client);
 else
 ev_tcp_handle_set_on_close(client, on_close);
}
int main(void) {
 ev_context *ctx = ev_get_context();
 ev_tcp_server server;
 int err = 0;
 if ((err = ev_tcp_server_init(&server, ctx, 128)) < 0) {
 fprintf(stderr, "ev_tcp_server_init failed: %s", ev_tcp_err(err));
 exit(EXIT_FAILURE);
 }
 // To set TLS using OpenSSL
 // struct ev_tls_options tls_opt = {
 // .ca = CA,
 // .cert = CERT,
 // .key = KEY
 // };
 // tls_opt.protocols = EV_TLSv1_2|EV_TLSv1_3;
 // ev_tcp_server_set_tls(&server, &tls_opt);
 int err = ev_tcp_server_listen(&server, HOST, PORT, on_connection);
 if (err < 0)
 exit(EXIT_FAILURE);
 // Blocking call
 ev_tcp_server_run(&server);
 // This could be registered to a SIGINT|SIGTERM signal notification
 // to stop the server with Ctrl+C
 ev_tcp_server_stop(&server);
 return 0;
}

Simple hello-world TCP client reading from STDIN

#include <stdio.h>
#include <stdlib.h>
#define EV_SOURCE // add before ev_tcp
#define EV_TCP_SOURCE // add before ev_tcp
#include "../ev.h"
#include "../ev_tcp.h"
#define HOST "127.0.0.1"
#define PORT 5959
#define BUFSIZE 256
// STDIN buffer
static unsigned char buf[BUFSIZE];
// STDIN handling callback
static void on_stdin(ev_context *, void *);
// TCP handling callback
static void on_tcp_recv(ev_tcp_handle *);
static void on_tcp_send(ev_tcp_handle *);
static void on_tcp_close(ev_tcp_handle *, int);
static void on_tcp_close(ev_tcp_handle *client, int err) {
 (void)client;
 if (err == EV_TCP_SUCCESS)
 printf("Connection closed\n");
 else
 printf("Connection closed: %s\n", ev_tcp_err(err));
}
static void on_tcp_send(ev_tcp_handle *client) {
 printf("Written %s", client->buffer.buf);
 ev_tcp_zero_buffer(client);
 // Re-arm TCP client for read
 (void)ev_tcp_queue_read(client);
}
static void on_tcp_recv(ev_tcp_handle *client) {
 printf("Response (%li bytes) => %s", client->buffer.size, client->buffer.buf);
 ev_tcp_zero_buffer(client);
}
static void on_stdin(ev_context *ctx, void *ptr) {
 ssize_t n = 0;
 ev_tcp_handle *handle = ptr;
 int fd = fileno(stdin);
 // Read incoming stream of bytes from user input
 n = read(fd, buf, sizeof(buf));
 if (n < 0) {
 if (errno != EAGAIN && errno != EWOULDBLOCK)
 goto err;
 }
 // 0 bytes read means disinput by the client
 if (n == 0) {
 ev_del_fd(ctx, fd);
 return;
 }
 // Close the input and release the resource
 if (strncmp((char *)buf, "quit", 4) == 0) {
 ev_del_fd(ctx, fd);
 exit(EXIT_SUCCESS);
 }
 ev_tcp_fill_buffer(handle, buf, n);
 ev_tcp_queue_write(handle);
 return;
err:
 fprintf(stderr, "read(2) - error reading data: %s\n", strerror(errno));
}
int main(void) {
 ev_context *ctx = ev_get_context();
 ev_tcp_handle client = {.ctx = ctx, .addr = HOST, .port = PORT};
 int err = 0;
 if ((err = ev_tcp_connect(&client, on_tcp_recv, on_tcp_send)) < 0) {
 fprintf(stderr, "ev_tcp_connect failed: %s", ev_tcp_err(err));
 exit(EXIT_FAILURE);
 }
 ev_tcp_handle_set_on_close(&client, on_tcp_close);
 err = ev_register_event(ctx, fileno(stdin), EV_READ, on_stdin, &client);
 if (err < 0) {
 fprintf(stderr, "ev_register_event failed: %s", ev_tcp_err(err));
 exit(EXIT_FAILURE);
 }
 // Blocking call
 ev_run(ctx);
 return 0;
}

Take a look to examples/ directory for more snippets.

Roadmap

  • (Re)Move server abstraction on generic ev_tcp_handle, add client
  • UDP helper APIs
  • Improve error handling
  • Documentation

About

Lightweight event-loop library based on multiplexing IO

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

AltStyle によって変換されたページ (->オリジナル) /