最近在学习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; }
原文:http://xzx951753.blog.51cto.com/3231458/1713960