#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 server; * 3. Call setup with a port to listen on: server.setup(1234) * 4. Call update regurlarly to update the event list * * * 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)); * } * }; * * * */ extern "C" { #include #include #include #include #include #include } #include "Connection.h" #include using std::map; template 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 connections; }; template Server::Server() :base(NULL) ,listener(NULL) ,signal_event(NULL) { } template Server::~Server() { if(signal_event != NULL) { event_free(signal_event); } if(listener != NULL) { evconnlistener_free(listener); } if(base != NULL) { event_base_free(base); } } template bool Server::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 void Server::update() { if(base != NULL) { event_base_loop(base, EVLOOP_NONBLOCK); } } template void Server::addConnection(evutil_socket_t fd, T* connection) { connections.insert(std::pair(fd, connection)); } template void Server::removeConnection(evutil_socket_t fd) { connections.erase(fd); } template void Server::sendToAllClients(const char* data, size_t len) { typename map::iterator it = connections.begin(); while(it != connections.end()) { it->second->send(data, len); ++it; } } // ------------------------------------ template void Server::listenerCallback( struct evconnlistener* listener ,evutil_socket_t fd ,struct sockaddr* saddr ,int socklen ,void* data ) { Server* server = static_cast* >(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 void Server::signalCallback(evutil_socket_t sig, short events, void* data) { Server* server = static_cast *>(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 void Server::writeCallback(struct bufferevent* bev, void* data) { struct evbuffer* output = bufferevent_get_output(bev); if(evbuffer_get_length(output) == 0) { } printf("write callback.\n"); } template void Server::readCallback(struct bufferevent* bev, void* connection) { T* conn = static_cast(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 void Server::eventCallback(struct bufferevent* bev, short events, void* data) { T* conn = static_cast(data); Server* server = static_cast* >(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