libevent C++封装

最近在学习libevent的过程中,碰到许多问题,如接收数据不完整,如何接收并回复来自client的数据等一些问题,还有就是关于read_cb该如何写的问题,最后总结了一下,封装成一个类,下面说一下怎样使用。

源文件:libSocket.h libSocket.cpp MyEvent.h MyEvent.cpp 这4个文件是自己写的,封装的目的是为了让整个过程更清晰易懂。

以下是libSocket类, 该类只有两个函数,GetFileDescriptionByListen是监听,s_ip是允许访问的IP,可以是"127.0.0.1"或"0.0.0.0", queue是listen的队列大小。函数最终返回一个正在监听的文件描述符。 GetFileDescriptionByConnect 是连接函数,s_host是要连接的服务端IP,最终也是返回一个已连接的文件描述符。

#ifndef LIBSOCKET_H_
#define LIBSOCKET_H_
#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>


using namespace std;

class libSocket {
public:
	libSocket();
	virtual ~libSocket();
	/*侦听端口*/
	int GetFileDescriptionByListen(const char* s_ip, int16_t port, int queue);
	int GetFileDescriptionByConnect(const char* s_host, int16_t port);
};

#endif /* LIBSOCKET_H_ */

以下是libSocket.cpp

#include "libSocket.h"

libSocket::libSocket() {
	// TODO Auto-generated constructor stub

}

libSocket::~libSocket() {
	// TODO Auto-generated destructor stub
}

int libSocket::GetFileDescriptionByListen(const char* s_ip,int16_t port,int queue){

	int fd = 0;					/*服务端文件描述符*/
	sockaddr_in server_addr;

	bzero(&server_addr,sizeof(server_addr));			/*将结构实例清零*/
	server_addr.sin_family = AF_INET;					/*使用AF_INET网络协议族*/
	server_addr.sin_addr.s_addr = inet_addr(s_ip);	/*网络地址 ,使用的in_addr结构体,INADDR_ANY表示监听任意来源IP*/
	server_addr.sin_port = htons(port);					/*端口号,必须采用网络数据格式,网络数据格式用htons()函数转换*/

	/*生成描述符*/
	if ( !(fd = socket(AF_INET,SOCK_STREAM,0)) ){
		cerr << strerror(errno) << endl;
		return -1;
	}

	/*绑定本地服务端地址和端口*/
	if ( bind(fd,(sockaddr *)(&server_addr),sizeof(server_addr)) != 0 ){
		cerr << strerror(errno) << endl;
		return -2;
	}

	/*在本地端口上开始监听*/
	if ( listen(fd,queue) != 0 ){
		cerr << strerror(errno) << endl;
		return -3;
	}

	return fd;
}

int libSocket::GetFileDescriptionByConnect(const char *host,int16_t port){
	int fd = 0;
	/*定义了一个存储服务端信息的结构实例*/
	struct sockaddr_in server_addr;
	/*用于接收gethostbyname返回的指针*/
	hostent* p_host;

	//域名解析
	if( (p_host = gethostbyname(host)) == NULL ){
		cerr << strerror(errno) << endl;
		return -1;
	}

	//向系统申请socket
	if( ( fd = socket(AF_INET,SOCK_STREAM,0) ) < 1 ){
		cerr << strerror(errno) << endl;
		return -2;
	}

	/*设置目标服务端*/
	bzero(&server_addr,sizeof(server_addr));
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(port);
	server_addr.sin_addr = *((struct in_addr *)p_host->h_addr);

	/*建立TCP连接*/
	if( connect(fd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr)) != 0 ){
		cerr << strerror(errno) << endl;
		return -3;
	}
	return fd;
}

下面的才是要说的主要MyEvent.

MyEvent.h

这也是很简单的几个成员函数

  1. 先使用BindEventBase,使用前先创建好event_base.

  2. 使用CreateEventForListen创建一个网络事件,需要告诉该事件要在本地监听的IP,和端口,在这个方法的实现中,使用到了上面的libSocket.

  3. 该类是用作于父类的,使用时,应在子类中重写父类的三个方法。即CallBackOfRead,CallBackOfWrite,CallBackOfError.

#ifndef MYEVENT_H_
#define MYEVENT_H_

#include <event.h>
#include "libSocket.h"


class MyEvent {
public:
	void CreateEventForListen(const char* SrcIPAddress,int16_t ListenPort);
	void BindEventBase(event_base* base){ this->base = base; } //绑定事件堆
	event_base* GetEventBase(void);

	/*以下三个函数在子类继承后重写父类的虚函数*/
	virtual void CallBackOfRead(bufferevent* bev,void* arg)=0;  //读取时触发事件
	virtual void CallBackOfWrite(bufferevent* bev,void* arg)=0;	//写入事件完成后触发事件
	virtual void CallBackOfError(bufferevent* bev,short event, void* arg)=0; //出现错误或对方关闭事件时触发事件

private:
	event_base* base;
};





#endif /* MYEVENT_H_ */

MyEvent.cpp

#include "MyEvent.h"

void onRead(bufferevent* bev,void* arg){	
	MyEvent* myevent = (MyEvent*)arg;
	myevent->CallBackOfRead(bev,arg);
}

void onWrite(bufferevent* bev,void* arg){	
	MyEvent* myevent = (MyEvent*)arg;
	myevent->CallBackOfWrite(bev,arg);
}

void onError(bufferevent* bev, short event, void* arg){  
	MyEvent* myevent = (MyEvent*)arg;
	myevent->CallBackOfError(bev,event,arg);
}

void onAccept( int fd ,short event, void* arg ){	
	MyEvent* myevent = (MyEvent*)arg;
	int client_fd;
	sockaddr_in client_in;
	socklen_t client_len = sizeof(client_in);
	client_fd = accept(fd,(struct sockaddr*)&client_in,&client_len);   //接受连接请求
	bufferevent* buffer_ev = bufferevent_socket_new(myevent->GetEventBase(),client_fd,BEV_OPT_CLOSE_ON_FREE);                     //创建数据I/O事件
	bufferevent_setcb(buffer_ev,onRead,onWrite,onError,arg);  //数据I/O事件的回调函数
	bufferevent_enable(buffer_ev,EV_READ | EV_PERSIST);	//启用数据IO
}


event_base* MyEvent::GetEventBase(void){
	return this->base;
}

void MyEvent::CreateEventForListen(const char* SrcIPAddress,int16_t ListenPort){ 
	libSocket* socket = new libSocket();
	int fd = socket->GetFileDescriptionByListen(SrcIPAddress,ListenPort,1024);
	if ( !this->base ){
		cout << "event_base_error" << endl;
		return ;
	}
	event* ev = event_new( this->base , fd , EV_READ | EV_PERSIST, onAccept , this );
	event_add(ev,NULL);
}

下面我们写一个子类来继承MyEvent

这里要特别注意的是:CallBackOfWrite()的重写,当有大量的数据传入缓冲区时,该函数会被多次触发,一次是接收不完那么多数据的,比如传入1000000个字节,每次触发该函数时,可能只能接收到4000个字节左右,所以不要在这里处理你所需要的最终数据,在这里你应该把所有的数据压入一个容器堆中,或者写入本地,如果写入本地,也不要在这个函数中打开文件描述符,因为是多次触发,最终得到的结果会是一小块,更不能在此关闭文件描述符,除非能够判断文件已经传输完毕。

#ifndef TEST_H_
#define TEST_H_
#include "MyEvent.h"

class test : public MyEvent {
public:
	void CallBackOfRead(bufferevent* bev,void* arg){
		evbuffer* input = bufferevent_get_input(bev);
		char buffer[1024];
		memset(buffer,'\0',sizeof(buffer));
		int r_len = 0;
		while ( ( r_len = evbuffer_remove( input, buffer, 1024  ) ) > 0 ){
			cout << buffer;
			memset(buffer,'\0',sizeof(buffer));
			
	}

	void CallBackOfWrite(bufferevent* bev,void* arg){
		cout << "write" << endl;
	}

	void CallBackOfError(bufferevent* bev,short event,void* arg){
		cout << "error" << endl;
	}
};

#endif /* TEST_H_ */

以下是主函数部分main.cpp

#include <event.h>
#include <iostream>
#include "libSocket.h"
#include "test.h"


int main(void){
	event_base* base = event_base_new();
	test* MyTest = new test();
	MyTest->BindEventBase(base);
	MyTest->CreateEventForListen("0.0.0.0",2111);
	event_base_dispatch(base);
	return 0;

}

猜你喜欢

转载自blog.csdn.net/xumaojun/article/details/84340791