Skip to content

Instantly share code, notes, and snippets.

@roxlu
Created June 4, 2012 12:52
Show Gist options
  • Save roxlu/2868160 to your computer and use it in GitHub Desktop.
Save roxlu/2868160 to your computer and use it in GitHub Desktop.

Revisions

  1. roxlu created this gist Jun 4, 2012.
    35 changes: 35 additions & 0 deletions Connection.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,35 @@
    #ifndef LIBEVENT_CONNECTIONH
    #define LIBEVENT_CONNECTIONH

    extern "C" {
    #include <event2/event.h>
    #include <event2/buffer.h>
    #include <event2/bufferevent.h>
    }
    #include <string>

    class Connection {
    public:
    Connection(evutil_socket_t fd, struct bufferevent* bev, void* server);
    void send(const char* data, size_t numBytes);

    struct bufferevent* bev;
    evutil_socket_t fd;
    void* server;
    };


    inline Connection::Connection(evutil_socket_t fd, bufferevent* bev, void* server)
    {
    this->bev = bev;
    this->fd = fd;
    this->server = server;
    printf("Created connection with server ref: %p\n", server);
    }

    inline void Connection::send(const char* data, size_t numBytes) {
    if(bufferevent_write(bev, data, numBytes) == -1) {
    printf("Error while sending in Connection::send()\n");
    }
    }
    #endif
    251 changes: 251 additions & 0 deletions Server.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,251 @@
    #ifndef LIBEVENT_SERVERH
    #define LIBEVENT_SERVERH

    /**
    *
    *
    * TCP client-server wrapper; handy when you need to do some
    * basic TCP communication. But this can easily be used to handle
    * hundreds of simultanious connections.
    *
    * Though be aware; if you need zero copy buffer handling you need
    * to change the way we handle the buffers.
    *
    *
    * Usage:
    * 1. Create a new class which inherits from Connection.
    * 2. Create a server instance: Server<YourConnectionClass> server;
    * 3. Call setup with a port to listen on: server.setup(1234)
    * 4. Call update regurlarly to update the event list
    *
    * <example>
    * class MyConnection : public Connection {
    * public:
    * MyConnection(evutil_socket_t fd, struct bufferevent* bev, void* server)
    * :Connection(fd, bev, server)
    * {
    * }
    *
    * void onRead(const char* data, const size_t& numBytes) {
    * const char* msg = "hoi\n";
    * send(msg,strlen(msg));
    * }
    * };
    * </example>
    *
    *
    */

    extern "C" {
    #include <sys/socket.h>
    #include <event2/bufferevent.h>
    #include <event2/buffer.h>
    #include <event2/listener.h>
    #include <event2/util.h>
    #include <event2/event.h>
    }

    #include "Connection.h"
    #include <map>
    using std::map;

    template<class T>
    class Server {
    public:

    Server();
    ~Server();

    bool setup(const unsigned short& port) ;
    void update();
    void sendToAllClients(const char* data, size_t len);
    void addConnection(evutil_socket_t fd, T* connection);
    void removeConnection(evutil_socket_t fd);

    static void listenerCallback(
    struct evconnlistener* listener
    ,evutil_socket_t socket
    ,struct sockaddr* saddr
    ,int socklen
    ,void* server
    );

    static void signalCallback(evutil_socket_t sig, short events, void* server);
    static void writeCallback(struct bufferevent*, void* server);
    static void readCallback(struct bufferevent*, void* connection);
    static void eventCallback(struct bufferevent*, short, void* server);

    struct sockaddr_in sin;
    struct event_base* base;
    struct event* signal_event;
    struct evconnlistener* listener;

    map<evutil_socket_t, T*> connections;
    };

    template<class T>
    Server<T>::Server()
    :base(NULL)
    ,listener(NULL)
    ,signal_event(NULL)
    {
    }


    template<class T>
    Server<T>::~Server() {
    if(signal_event != NULL) {
    event_free(signal_event);
    }

    if(listener != NULL) {
    evconnlistener_free(listener);
    }

    if(base != NULL) {
    event_base_free(base);
    }
    }

    template<class T>
    bool Server<T>::setup(const unsigned short& port) {

    base = event_base_new();
    if(!base) {
    printf("Server: cannot create base.\n");
    return false;
    }

    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_port = htons(port);

    listener = evconnlistener_new_bind(
    base
    ,Server::listenerCallback
    ,(void*)this
    ,LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE
    ,-1
    ,(struct sockaddr*)&sin
    ,sizeof(sin)
    );

    if(!listener) {
    printf("Cannot create listener.\n");
    return false;
    }

    signal_event = evsignal_new(base, SIGINT, signalCallback, (void*)this);
    if(!signal_event || event_add(signal_event, NULL) < 0) {

    printf("Cannog create signal event.\n");
    return false;
    }
    return true;
    }


    template<class T>
    void Server<T>::update() {
    if(base != NULL) {
    event_base_loop(base, EVLOOP_NONBLOCK);
    }
    }

    template<class T>
    void Server<T>::addConnection(evutil_socket_t fd, T* connection) {
    connections.insert(std::pair<evutil_socket_t, T*>(fd, connection));
    }

    template<class T>
    void Server<T>::removeConnection(evutil_socket_t fd) {
    connections.erase(fd);
    }

    template<class T>
    void Server<T>::sendToAllClients(const char* data, size_t len) {
    typename map<evutil_socket_t, T*>::iterator it = connections.begin();
    while(it != connections.end()) {
    it->second->send(data, len);
    ++it;
    }
    }

    // ------------------------------------

    template<class T>
    void Server<T>::listenerCallback(
    struct evconnlistener* listener
    ,evutil_socket_t fd
    ,struct sockaddr* saddr
    ,int socklen
    ,void* data
    )
    {
    Server<T>* server = static_cast<Server<T>* >(data);
    struct event_base* base = (struct event_base*) server->base;
    struct bufferevent* bev;

    bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
    if(!bev) {
    event_base_loopbreak(base);
    printf("Error constructing bufferevent!\n");
    return;
    }

    T* conn = new T(fd, bev, (void*)server);
    server->addConnection(fd, conn);

    bufferevent_setcb(bev, Server::readCallback, Server::writeCallback, Server::eventCallback, (void*)conn);
    bufferevent_enable(bev, EV_WRITE);
    bufferevent_enable(bev, EV_READ);
    }

    template<class T>
    void Server<T>::signalCallback(evutil_socket_t sig, short events, void* data) {
    Server<T>* server = static_cast<Server<T> *>(data);
    struct event_base* base = server->base;
    struct timeval delay = {2,0};
    printf("Caught an interrupt signal; exiting cleanly in two seconds.\n");
    event_base_loopexit(base, &delay);
    }

    template<class T>
    void Server<T>::writeCallback(struct bufferevent* bev, void* data) {
    struct evbuffer* output = bufferevent_get_output(bev);
    if(evbuffer_get_length(output) == 0) {

    }
    printf("write callback.\n");
    }

    template<class T>
    void Server<T>::readCallback(struct bufferevent* bev, void* connection) {
    T* conn = static_cast<T*>(connection);
    struct evbuffer* buf = bufferevent_get_input(bev);
    char readbuf[1024];
    size_t read = 0;

    while( (read = evbuffer_remove(buf, &readbuf, sizeof(readbuf))) > 0) {
    conn->onRead(readbuf, read);
    }
    }

    template<class T>
    void Server<T>::eventCallback(struct bufferevent* bev, short events, void* data) {
    T* conn = static_cast<T*>(data);
    Server<T>* server = static_cast<Server<T>* >(conn->server);

    if(events & BEV_EVENT_EOF) {
    server->removeConnection(conn->fd);
    bufferevent_free(bev);
    }
    else if(events & BEV_EVENT_ERROR) {
    printf("Got an error on the connection: %s\n", strerror(errno));
    }
    else {
    printf("unhandled.\n");
    }
    }

    #endif