前言
本文不会讲libevent如何使用,只提供一个简单的c++ libevent 封装。
封装的功能
提供网络连接事件,网络断开事件,读数据事件,使用时候重载该虚函数即可。
提供定时器功能,定时向连接的客户端发送数据。
主要逻辑
1,libeventSer—
该类是网络服务的主体,对外提供StartListen(int Port);和AddTimerEvent()两个接口,
使用时候继承改类,重写虚函数即可。
关于回调为静态函数问题:event_assign()中回调函数,固定类型,而作为一个类的成员函数,会多一个this,导致报错。
class LibeventSer
{
public:
LibeventSer();
~LibeventSer();
bool StartListen(int Port); // 监听端口
bool AddTimerEvent(void(*ptr)(int, short, void *), timeval tv, bool once);
protected:
//读取完数据后,会调用该函数
virtual void ReadEvent(Conn * buf) { }
//断开连接(客户自动断开或异常断开)后,会调用该函数
virtual void CloseEvent(Conn *buf) { }
virtual void ConnectionEvent(Conn *buf) {}
private:
int tcp_server_init(int port, int listen_num);
struct event_base * m_base;
struct event * m_timer;
static void listener_cb(int fd, short events, void* arg);
static void read_cb(int fd, short events, void *arg);
};
Conn为连接类,当获得一个客户端连接时候,在回调函数中实例化一个新的Conn类添加到事件循环中,故每个Conn都有自己的套接字和对应的事件。当每个Conn的读事件发生时候,还需要回调到libeventSer的读写事件中,故还需要每个连接类对应的LibeventSer 的指针。读回调会将读取的数据保存到data_buf中,回调给LibeventSer 的虚函数。
class Conn
{
public:
int m_fd;//连接套接字
struct event *m_event;
LibeventSer *m_ser; //回调
char data_buf[100];
int data_len;
Conn(int fd, struct event *event1, LibeventSer *ser) { m_fd = fd; m_event = event1; m_ser = ser; };
~Conn() { event_free(m_event); m_ser = nullptr; };
void Send_data();//发送接收到的数据
void Send_data(char *buf, int len);//发送buf
private:
};
完整代码
LibeventSer.h
#pragma once
#include<list>
#include <iostream>
#include <event.h>
#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <event2/listener.h>
#include <event2/util.h>
#include <event2/event.h>
#pragma comment(lib, "libevent.lib")
#pragma comment(lib, "libevent_core.lib")
#pragma comment(lib, "libevent_extras.lib")
class LibeventSer;
class Conn;
class Conn
{
public:
int m_fd;//连接套接字
struct event *m_event; //监听
LibeventSer *m_ser; //回调
char data_buf[100];
int data_len;
Conn(int fd, struct event *event1, LibeventSer *ser) { m_fd = fd; m_event = event1; m_ser = ser; };
~Conn() { event_free(m_event); m_ser = nullptr; };
void Send_data();//发送接收到的数据
void Send_data(char *buf, int len);//发送buf
private:
};
class LibeventSer
{
public:
LibeventSer();
~LibeventSer();
bool StartListen(int Port); // 监听端口
bool AddTimerEvent(void(*ptr)(int, short, void *), timeval tv, bool once);
protected:
//读取完数据后,会调用该函数
virtual void ReadEvent(Conn * buf) { }
//断开连接(客户自动断开或异常断开)后,会调用该函数
virtual void CloseEvent(Conn *buf) { }
virtual void ConnectionEvent(Conn *buf) {}
private:
int tcp_server_init(int port, int listen_num);
struct event_base * m_base;
struct event * m_timer;
static void listener_cb(int fd, short events, void* arg);
static void read_cb(int fd, short events, void *arg);
};
LibeventSer.cpp
#include"LibeventSer.h"
#define _WINSOCK_DEPRECATED_NO_WARNINGS
void Conn::Send_data()
{
::send(m_fd, data_buf, data_len, 0);
}
void Conn::Send_data(char *buf, int len)
{
::send(m_fd, buf, len, 0);
}
int LibeventSer::tcp_server_init(int port, int listen_num)
{
int errno_save;
int listener;
listener = ::socket(AF_INET, SOCK_STREAM, 0);
if (listener == -1)
return -1;
//允许多次绑定同一个地址。要用在socket和bind之间
evutil_make_listen_socket_reuseable(listener);
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = 0;
sin.sin_port = htons(port);
if (::bind(listener, (struct sockaddr*)&sin, sizeof(sin)) < 0)
{
errno_save = errno;
evutil_closesocket(listener);
errno = errno_save;
return -1;
}
if (::listen(listener, listen_num) < 0)
{
errno_save = errno;
evutil_closesocket(listener);
errno = errno_save;
return -1;
}
//跨平台统一接口,将套接字设置为非阻塞状态
evutil_make_socket_nonblocking(listener);
return listener;
}
LibeventSer::LibeventSer()
{
m_base = event_base_new();
m_timer = new event;
}
LibeventSer::~LibeventSer()
{
event_base_free(m_base);
}
bool LibeventSer::AddTimerEvent(void(*ptr)(int, short, void *), timeval tv, bool once)
{
int flag = 0;
if (!once)
flag = EV_PERSIST;
//新建定时器信号事件
event_assign(m_timer, m_base, -1, flag, ptr, (void*)this);
if (event_add(m_timer, &tv) < 0)
{
// event_del(m_timer);
return false;
}
return true;
}
void LibeventSer::read_cb(int fd, short events, void *arg)
{
//char buf[100] = {0};
Conn *con_buf = (Conn *)arg;
con_buf->data_len = recv(fd, con_buf->data_buf, sizeof(con_buf->data_buf), 0);
if (con_buf->data_len <= 0)
{
closesocket(fd);
con_buf->m_ser->CloseEvent(con_buf);
delete con_buf;
}
else
{
printf("[libevent-recv]msg: %s-len:%d\n", con_buf->data_buf,con_buf->data_len);
con_buf->m_ser->ReadEvent(con_buf);
memset(con_buf->data_buf,0,con_buf->data_len);
con_buf->data_len = 0;
}
}
void LibeventSer::listener_cb(int fd, short events, void* arg)
{
evutil_socket_t sockfd;
struct sockaddr_in client;
socklen_t len = sizeof(client);
sockfd = ::accept(fd, (struct sockaddr*)&client, &len);
evutil_make_socket_nonblocking(sockfd);
printf("[libevent]:accept a client %d\n", sockfd);
LibeventSer *ser_buf = (LibeventSer*)arg;
//仅仅是为了动态创建一个event结构体
struct event *ev = event_new(NULL, -1, 0, NULL, NULL);
//将动态创建的结构体作为event的回调参数
Conn *con = new Conn(sockfd, ev, ser_buf);
ser_buf->ConnectionEvent(con);
event_assign(ev, ser_buf->m_base, sockfd, EV_READ | EV_PERSIST,
LibeventSer::read_cb, (void*)con);
event_add(ev, NULL);
}
bool LibeventSer::StartListen(int Port)
{
int listener = tcp_server_init(Port, 500);
if (listener == -1)
{
perror(" **error**:tcp_server_init error ");
return false;
}
struct event* ev_listen = event_new(m_base, listener, EV_READ | EV_PERSIST, LibeventSer::listener_cb, this);
event_add(ev_listen, NULL);
event_base_dispatch(m_base);
//printf("over\n");
return true;
}
tcp_server.h
该类继承自libeventSer,由于有定时向连接客户端发送数据,故需要一个容器保存Conn,同时编写定时器回调函数。
#pragma once
#include"LibeventSer.h"
#include<vector>
#include<algorithm>
class Tcp_server :public LibeventSer
{
public:
std::vector<Conn *> m_list_conn;
Tcp_server() {};
~Tcp_server() {};
static void TimeOutCb(int id, int short events, void *data);
void RUN();
private:
void ReadEvent(Conn * con);
void CloseEvent(Conn * buf);
void ConnectionEvent(Conn *buf);
};
Tcp_Server.cpp
#include"Tcp_Server.h"
void Tcp_server::TimeOutCb(int id, int short events, void *data)
{
Tcp_server *me = (Tcp_server*)data;
char temp[33] = "hello, world\n";
if (!me->m_list_conn.empty())
printf(temp);
for (int i = 0; i < me->m_list_conn.size(); i++)
me->m_list_conn[i]->Send_data(temp, strlen(temp));
};
void Tcp_server::ReadEvent(Conn * con)
{
printf("%s-len:%d\n", con->data_buf, con->data_len);
con->Send_data();
}
void Tcp_server::CloseEvent(Conn * buf)
{
printf("socket close\n");
auto re = std::find_if(m_list_conn.begin(), m_list_conn.end(), [=](Conn * elem)
{
if (elem == buf)
return true;
return false;
});
if (re != m_list_conn.end())
m_list_conn.erase(re);
else
printf("pragram error");
};
void Tcp_server::ConnectionEvent(Conn *buf)
{
m_list_conn.push_back(buf);
}
void Tcp_server::RUN()
{
timeval tv = { 5,0 };
this->AddTimerEvent(TimeOutCb, tv, false);
this->StartListen(40704);
}
test
测试效果 监听端口:40704
Tcp_server *m_ser=new Tcp_server();
m_ser->RUN();