Skip to content

Instantly share code, notes, and snippets.

@roxlu
Created June 4, 2012 12:52
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save roxlu/2868160 to your computer and use it in GitHub Desktop.
Save roxlu/2868160 to your computer and use it in GitHub Desktop.
Basic libEvent TCP server/client wrapper
#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
#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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment