Skip to content

Instantly share code, notes, and snippets.

@rsms
Created March 25, 2009 23:39
Show Gist options
  • Save rsms/85790 to your computer and use it in GitHub Desktop.
Save rsms/85790 to your computer and use it in GitHub Desktop.
/* libevent echo server example using buffered events. */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
/* Required by event.h. */
#include <sys/time.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <err.h>
/* Libevent. */
#include <event.h>
/* Port to listen on. */
#define SERVER_PORT 5555
/**
* A struct for client specific data, also includes pointer to create
* a list of clients.
*/
struct client {
/* The clients socket. */
int fd;
/* The bufferedevent for this client. */
struct bufferevent *buf_ev;
};
/**
* Set a socket to non-blocking mode.
*/
int setnonblock(int fd) {
int flags;
flags = fcntl(fd, F_GETFL);
if (flags < 0)
return flags;
flags |= O_NONBLOCK;
if (fcntl(fd, F_SETFL, flags) < 0)
return -1;
return 0;
}
/**
* Called by libevent when there is data to read.
*/
void buffered_on_read(struct bufferevent *bev, void *arg) {
/* Write back the read buffer. It is important to note that
* bufferevent_write_buffer will drain the incoming data so it
* is effectively gone after we call it. */
bufferevent_write_buffer(bev, bev->input);
}
/**
* Called by libevent when the write buffer reaches 0. We only
* provide this because libevent expects it, but we don’t use it.
*/
void buffered_on_write(struct bufferevent *bev, void *arg) {
}
/**
* Called by libevent when there is an error on the underlying socket
* descriptor.
*/
void buffered_on_error(struct bufferevent *bev, short what, void *arg) {
struct client *client = (struct client *)arg;
if (what & EVBUFFER_EOF) {
/* Client disconnected, remove the read event and the
* free the client structure. */
printf("Client disconnected.\n");
}
else {
warn("Client socket error, disconnecting.\n");
}
bufferevent_free(client->buf_ev);
close(client->fd);
free(client);
}
/**
* This function will be called by libevent when there is a connection
* ready to be accepted.
*/
void on_accept(int fd, short ev, void *arg) {
int client_fd;
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
struct client *client;
client_fd = accept(fd, (struct sockaddr *)&client_addr, &client_len);
if (client_fd < 0) {
warn("accept failed");
return;
}
/* Set the client socket to non-blocking mode. */
if (setnonblock(client_fd) < 0)
warn("failed to set client socket non-blocking");
/* We’ve accepted a new client, create a client object. */
client = calloc(1, sizeof(*client));
if (client == NULL)
err(1, "malloc failed");
client->fd = client_fd;
/* Create the buffered event.
*
* The first argument is the file descriptor that will trigger
* the events, in this case the clients socket.
*
* The second argument is the callback that will be called
* when data has been read from the socket and is available to
* the application.
*
* The third argument is a callback to a function that will be
* called when the write buffer has reached a low watermark.
* That usually means that when the write buffer is 0 length,
* this callback will be called. It must be defined, but you
* don’t actually have to do anything in this callback.
*
* The fourth argument is a callback that will be called when
* there is a socket error. This is where you will detect
* that the client disconnected or other socket errors.
*
* The fifth and final argument is to store an argument in
* that will be passed to the callbacks. We store the client
* object here.
*/
client->buf_ev = bufferevent_new(client_fd, buffered_on_read,
buffered_on_write, buffered_on_error, client);
/* We have to enable it before our callbacks will be
* called. */
bufferevent_enable(client->buf_ev, EV_READ);
printf("Accepted connection from %s\n",
inet_ntoa(client_addr.sin_addr));
//sleep(1);
char data[] = "200 OK CONNECTION ESTABLISHED\n";
bufferevent_write(client->buf_ev, data, sizeof(data));
}
int main(int argc, char **argv) {
int listen_fd;
struct sockaddr_in listen_addr;
struct event ev_accept;
int reuseaddr_on;
/* Initialize libevent. */
event_init();
/* Create our listening socket. */
listen_fd = socket(AF_INET, SOCK_STREAM, 0);
if (listen_fd < 0)
err(1, "listen failed");
memset(&listen_addr, 0, sizeof(listen_addr));
listen_addr.sin_family = AF_INET;
listen_addr.sin_addr.s_addr = INADDR_ANY;
listen_addr.sin_port = htons(SERVER_PORT);
if (bind(listen_fd, (struct sockaddr *)&listen_addr,
sizeof(listen_addr)) < 0)
err(1, "bind failed");
if (listen(listen_fd, 5) < 0)
err(1, "listen failed");
reuseaddr_on = 1;
setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr_on,
sizeof(reuseaddr_on));
/* Set the socket to non-blocking, this is essential in event
* based programming with libevent. */
if (setnonblock(listen_fd) < 0)
err(1, "failed to set server socket to non-blocking");
/* We now have a listening socket, we create a read event to
* be notified when a client connects. */
event_set(&ev_accept, listen_fd, EV_READ|EV_PERSIST, on_accept, NULL);
event_add(&ev_accept, NULL);
/* Start the event loop. */
event_dispatch();
return 0;
}
# libevent echo server example using buffered events
SOURCES=libevent_echo.c
EXECUTABLE=libevent_echo
CFLAGS=-c -Wall -DDEBUG=1 -I/opt/local/include
LDFLAGS=-L/opt/local/lib -levent
OBJECTS=$(SOURCES:.c=.o)
all: $(SOURCES) $(EXECUTABLE)
$(EXECUTABLE): $(OBJECTS)
$(CC) $(LDFLAGS) $(OBJECTS) -o $@
.c.o:
$(CC) $(CFLAGS) $< -o $@
clean:
rm -rf *.o $(EXECUTABLE)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment