您的位置:首页 > 编程语言 > C语言/C++

libevent C++封装

2015-11-18 12:35 246 查看
最近在学习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
这也是很简单的几个成员函数
先使用BindEventBase,使用前先创建好event_base.

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

该类是用作于父类的,使用时,应在子类中重写父类的三个方法。即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;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息