2011/06/16

Echo server over libevent - sample code

一個簡易的echo server,使用了bufferevent。

Bufferevent
Bufferevents are higher level than evbuffers: each has an underlying evbuffer for reading and one for writing, and callbacks that are invoked under certain circumstances.

#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <event.h>
#include <event2/listener.h>

#define SVR_IP                         "127.0.0.1"
#define SVR_PORT                       10000
#define BUFF_SIZE                      1024

static void read_cb(struct bufferevent *bev, void *ctx) {
    char buff[BUFF_SIZE];
    int len;

    memset(buff, 0, sizeof(buff));

    /* Read data */
    len = bufferevent_read(bev, buff, sizeof(buff));
    printf("read: len=[%d] data=[%s]\n", len, buff);

    /* Write data */
    bufferevent_write(bev, buff, len);
}

static void write_cb(struct bufferevent *bev, void *ctx) {
    printf("write finished\n");
}

static void event_cb(struct bufferevent *bev, short events, void *ctx) {
    if (events & BEV_EVENT_EOF) {
        printf("client disconnect\n");
        bufferevent_free(bev);
    } else if (events & BEV_EVENT_TIMEOUT) {
        printf("client timeout\n");
        bufferevent_free(bev);
    } else {
        /* Other case, maybe error occur */
        bufferevent_free(bev);
    }
}

static void accept_cb(struct evconnlistener *lev, evutil_socket_t fd,
        struct sockaddr *sa, int socklen, void *ctx) {
    struct event_base *evbase = ctx;
    struct bufferevent *bev;
    struct timeval tv;

    printf("client connect from [%s:%u] over fd [%d]\n", 
            inet_ntoa(((struct sockaddr_in *) sa)->sin_addr), 
            (unsigned short) ntohs(((struct sockaddr_in *) sa)->sin_port), fd);

    /* Create socket-based buffer event */
    bev = bufferevent_socket_new(evbase, fd, BEV_OPT_CLOSE_ON_FREE);
    if (bev == NULL) {
        printf("bufferevent_socket_new() failed\n");
        evutil_closesocket(fd);
        return;
    }

    /* Set up callback function */
    bufferevent_setcb(bev, read_cb, write_cb, event_cb, NULL);

    /* Set up timeout for data read */
    tv.tv_sec = 5;
    tv.tv_usec = 0;
    bufferevent_set_timeouts(bev, &tv, NULL);

    /* Enable read event */
    bufferevent_enable(bev, EV_READ);
}

int main(void) {
    struct event_base *evbase;
    struct sockaddr_in sin;
    struct evconnlistener *lev;

    /* Initialize event base */
    if ((evbase = event_base_new()) == NULL) {
        printf("event_base_new() failed\n");
        return -1;
    }

    /* Set up server address */
    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = inet_addr(SVR_IP);
    sin.sin_port = htons(SVR_PORT);

    /* Bind socket */
    lev = evconnlistener_new_bind(evbase, accept_cb, evbase, 
            LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE, -1, 
            (struct sockaddr *) &sin, sizeof(sin));

    if (lev == NULL) {
        printf("bind() failed\n");
        return -1;
    } else {
        printf("bind to [%s:%u] successfully\n", SVR_IP, SVR_PORT);
    }

    /* Enter event loop */
    event_base_dispatch(evbase);

    return 0;
}


編譯 (指定library path)
gcc -g -Wall -o server server.c -levent -I/opt/libevent-2.0.10-stable/include/ -L/opt/libevent-2.0.10-stable/lib/


2 則留言:

  1. Libevent 2.0 supports multiple threads.
    How would you change your code to support multiple threads? Greetings.

    回覆刪除
    回覆
    1. As I know, if you want to read/write socket cross thread, you should perform some init. funciton:
      evthread_use_pthreads(),
      evthread_make_base_notifiable()

      and create object with thread safe flag:
      BEV_OPT_THREADSAFE,
      LEV_OPT_THREADSAFE

      刪除