libevent+多线程

最近在看memcached的源码,觉得它那种libevent+多线程的服务器模型真的很不错,我将这个模型封装成一个C++类,根据我的简单测试,这个模型的效率真的很不错,欢迎大家试用。

这个类的使用方法很简单(缺点是不太灵活),只要派生一个类,根据需要重写以下这几个虚函数就行了:

//新建连接成功后,会调用该函数
virtual void ConnectionEvent(Conn *conn) { }
//读取完数据后,会调用该函数
virtual void ReadEvent(Conn *conn) { }
//发送完成功后,会调用该函数(因为串包的问题,所以并不是每次发送完数据都会被调用)
virtual void WriteEvent(Conn *conn) { }
//断开连接(客户自动断开或异常断开)后,会调用该函数
virtual void CloseEvent(Conn *conn, short events) { }
//发生致命错误(如果创建子线程失败等)后,会调用该函数
//该函数的默认操作是输出错误提示,终止程序
virtual void ErrorQuit(const char *str);


如果大家有什么建议或意见,欢迎给我发邮件:aa1080711@163.com


上代码:

头文件:TcpEventServer.h

//TcpEventServer.h
#ifndef TCPEVENTSERVER_H_
#define TCPEVENTSERVER_H_

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <time.h>
#include <pthread.h>
#include <fcntl.h>

#include <map>
using std::map;

#include <event.h>
#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <event2/listener.h>
#include <event2/util.h>
#include <event2/event.h>

class TcpEventServer;
class Conn;
class ConnQueue;
struct LibeventThread;

//这个类一个链表的结点类,结点里存储各个连接的信息,
//并提供了读写数据的接口
class Conn
{
  //此类只能由TcpBaseServer创建,
  //并由ConnQueue类管理
  friend class ConnQueue;
  friend class TcpEventServer;

private:
  const int m_fd;				//socket的ID
  evbuffer *m_ReadBuf;		//读数据的缓冲区
  evbuffer *m_WriteBuf;		//写数据的缓冲区

  Conn *m_Prev;				//前一个结点的指针
  Conn *m_Next;				//后一个结点的指针
  LibeventThread *m_Thread;

  Conn(int fd=0);
  ~Conn();

public:
  LibeventThread *GetThread() { return m_Thread; }
  int GetFd() { return m_fd; }

  //获取可读数据的长度
  int GetReadBufferLen()
  { return evbuffer_get_length(m_ReadBuf); }

  //从读缓冲区中取出len个字节的数据,存入buffer中,若不够,则读出所有数据
  //返回读出数据的字节数
  int GetReadBuffer(char *buffer, int len)
  { return evbuffer_remove(m_ReadBuf, buffer, len); }

  //从读缓冲区中复制出len个字节的数据,存入buffer中,若不够,则复制出所有数据
  //返回复制出数据的字节数
  //执行该操作后,数据还会留在缓冲区中,buffer中的数据只是原数据的副本
  int CopyReadBuffer(char *buffer, int len)
  { return evbuffer_copyout(m_ReadBuf, buffer, len); }

  //获取可写数据的长度
  int GetWriteBufferLen()
  { return evbuffer_get_length(m_WriteBuf); }

  //将数据加入写缓冲区,准备发送
  int AddToWriteBuffer(char *buffer, int len)
  { return evbuffer_add(m_WriteBuf, buffer, len); }

  //将读缓冲区中的数据移动到写缓冲区
  void MoveBufferData()
  { evbuffer_add_buffer(m_WriteBuf, m_ReadBuf); }

};

//带头尾结点的双链表类,每个结点存储一个连接的数据
class ConnQueue
{
private:
  Conn *m_head;
  Conn *m_tail;
public:
  ConnQueue();
  ~ConnQueue();
  Conn *InsertConn(int fd, LibeventThread *t);
  void DeleteConn(Conn *c);
  //void PrintQueue();
};

//每个子线程的线程信息
struct LibeventThread
{
  pthread_t tid;				//线程的ID
  struct event_base *base;	//libevent的事件处理机
  struct event notifyEvent;	//监听管理的事件机
  int notifyReceiveFd;		//管理的接收端
  int notifySendFd;			//管道的发送端
  ConnQueue connectQueue;		//socket连接的链表

  //在libevent的事件处理中要用到很多回调函数,不能使用类隐含的this指针
  //所以用这样方式将TcpBaseServer的类指针传过去
  TcpEventServer *tcpConnect;	 //TcpBaseServer类的指针
};

class TcpEventServer
{
private:
  int m_ThreadCount;					//子线程数
  int m_Port;							//监听的端口
  LibeventThread *m_MainBase;			//主线程的libevent事件处理机
  LibeventThread *m_Threads;			//存储各个子线程信息的数组
  map<int, event*> m_SignalEvents;	//自定义的信号处理

public:
  static const int EXIT_CODE = -1;

private:
  //初始化子线程的数据
  void SetupThread(LibeventThread *thread);

  //子线程的入门函数
  static void *WorkerLibevent(void *arg);
  //(主线程收到请求后),对应子线程的处理函数
  static void ThreadProcess(int fd, short which, void *arg);
  //被libevent回调的各个静态函数
  static void ListenerEventCb(evconnlistener *listener, evutil_socket_t fd,
    sockaddr *sa, int socklen, void *user_data);
  static void ReadEventCb(struct bufferevent *bev, void *data);
  static void WriteEventCb(struct bufferevent *bev, void *data); 
  static void CloseEventCb(struct bufferevent *bev, short events, void *data);

protected:
  //这五个虚函数,一般是要被子类继承,并在其中处理具体业务的

  //新建连接成功后,会调用该函数
  virtual void ConnectionEvent(Conn *conn) { }

  //读取完数据后,会调用该函数
  virtual void ReadEvent(Conn *conn) { }

  //发送完成功后,会调用该函数(因为串包的问题,所以并不是每次发送完数据都会被调用)
  virtual void WriteEvent(Conn *conn) { }

  //断开连接(客户自动断开或异常断开)后,会调用该函数
  virtual void CloseEvent(Conn *conn, short events) { }

  //发生致命错误(如果创建子线程失败等)后,会调用该函数
  //该函数的默认操作是输出错误提示,终止程序
  virtual void ErrorQuit(const