-
-
Save bitkevin/97c9a83b29034e8f41049628c325a482 to your computer and use it in GitHub Desktop.
Basic libEvent TCP server/client wrapper
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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