一个简单的libevent网络库封装

前言

本文不会讲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();
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

工农村贴膜小哥

我倒是要看看是那个憨憨在给我打

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值