牛骨文教育服务平台(让学习变的简单)
博文笔记

multi-reactor服务器模型的C++封装类(libevent+多线程实现)

创建时间:2016-07-08 投稿人: 浏览次数:2343

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

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

  1. //新建连接成功后,会调用该函数  
  2. virtual void ConnectionEvent(Conn *conn) { }  
  3. //读取完数据后,会调用该函数  
  4. virtual void ReadEvent(Conn *conn) { }  
  5. //发送完成功后,会调用该函数(因为串包的问题,所以并不是每次发送完数据都会被调用)  
  6. virtual void WriteEvent(Conn *conn) { }  
  7. //断开连接(客户自动断开或异常断开)后,会调用该函数  
  8. virtual void CloseEvent(Conn *conn, short events) { }  


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


上代码:

头文件:MultiServer.h

  1. //MultiServer.h    
  2. #ifndef MULTISERVER_H_    
  3. #define MULTISERVER_H_    
  4.   
  5. #include <stdio.h>    
  6. #include <stdlib.h>    
  7. #include <unistd.h>    
  8. #include <string.h>    
  9. #include <errno.h>    
  10. #include <signal.h>    
  11. #include <time.h>    
  12. #include <pthread.h>    
  13. #include <fcntl.h>    
  14. #include <assert.h>  
  15.   
  16. #include <event.h>    
  17. #include <event2/bufferevent.h>    
  18. #include <event2/buffer.h>    
  19. #include <event2/listener.h>    
  20. #include <event2/util.h>    
  21. #include <event2/event.h>    
  22.   
  23. class MultiServer;    
  24. class Conn;    
  25. class ConnQueue;    
  26. struct LibeventThread;    
  27.   
  28. //这个类一个链表的结点类,结点里存储各个连接的信息,    
  29. //并提供了读写数据的接口    
  30. class Conn    
  31. {    
  32.     //此类只能由TcpBaseServer创建,    
  33.     //并由ConnQueue类管理    
  34.     friend class ConnQueue;    
  35.     friend class MultiServer;    
  36.   
  37. private:    
  38.     const int m_fd;             //socket的ID    
  39.     evbuffer *m_ReadBuf;        //读数据的缓冲区    
  40.     evbuffer *m_WriteBuf;       //写数据的缓冲区    
  41.   
  42.     Conn *m_Prev;               //前一个结点的指针    
  43.     Conn *m_Next;               //后一个结点的指针    
  44.     LibeventThread *m_Thread;    
  45.   
  46.     Conn(int fd=0);    
  47.     ~Conn();    
  48.   
  49. public:    
  50.     LibeventThread *GetThread() { return m_Thread; }    
  51.     int GetFd() { return m_fd; }    
  52.   
  53.     //获取可读数据的长度    
  54.     int GetReadBufferLen()    
  55.     { return evbuffer_get_length(m_ReadBuf); }    
  56.   
  57.     //从读缓冲区中取出len个字节的数据,存入buffer中,若不够,则读出所有数据    
  58.     //返回读出数据的字节数    
  59.     int GetReadBuffer(char *buffer, int len)    
  60.     { return evbuffer_remove(m_ReadBuf, buffer, len); }    
  61.   
  62.     //从读缓冲区中复制出len个字节的数据,存入buffer中,若不够,则复制出所有数据    
  63.     //返回复制出数据的字节数    
  64.     //执行该操作后,数据还会留在缓冲区中,buffer中的数据只是原数据的副本    
  65.     int CopyReadBuffer(char *buffer, int len)    
  66.     { return evbuffer_copyout(m_ReadBuf, buffer, len); }    
  67.   
  68.     //获取可写数据的长度    
  69.     int GetWriteBufferLen()    
  70.     { return evbuffer_get_length(m_WriteBuf); }    
  71.   
  72.     //将数据加入写缓冲区,准备发送    
  73.     int AddToWriteBuffer(char *buffer, int len)    
  74.     { return evbuffer_add(m_WriteBuf, buffer, len); }    
  75.   
  76.     //将读缓冲区中的数据移动到写缓冲区    
  77.     void MoveBufferData()    
  78.     { evbuffer_add_buffer(m_WriteBuf, m_ReadBuf); }    
  79.   
  80. };    
  81.   
  82. //带头尾结点的双链表类,每个结点存储一个连接的数据    
  83. class ConnQueue    
  84. {    
  85. private:    
  86.     Conn *m_head;    
  87.     Conn *m_tail;    
  88. public:    
  89.     ConnQueue();    
  90.     ~ConnQueue();    
  91.     Conn *InsertConn(int fd, LibeventThread *t);    
  92.     void DeleteConn(Conn *c);    
  93.     //void PrintQueue();    
  94. };    
  95.   
  96. //每个子线程的线程信息    
  97. struct LibeventThread    
  98. {    
  99.     pthread_t tid;              //线程的ID    
  100.     struct event_base *base;    //libevent的事件处理机    
  101.     struct event notifyEvent;   //监听管理的事件机    
  102.     int notifyReceiveFd;        //管理的接收端    
  103.     int notifySendFd;           //管道的发送端    
  104.     ConnQueue connectQueue;     //socket连接的链表    
  105.   
  106.     //在libevent的事件处理中要用到很多回调函数,不能使用类隐含的this指针    
  107.     //所以用这样方式将TcpBaseServer的类指针传过去    
  108.     MultiServer *tcpConnect;  //TcpBaseServer类的指针    
  109. };    
  110.   
  111. class MultiServer    
  112. {    
  113. private:  
  114.     static const int EXIT_CODE = -1;    
  115.     static const int MAX_SIGNAL = 256;  
  116.   
  117. private:    
  118.     int m_ThreadCount;                  //子线程数    
  119.     int m_Port;                         //监听的端口    
  120.     LibeventThread *m_MainBase;         //主线程的libevent事件处理机    
  121.     LibeventThread *m_Threads;          //存储各个子线程信息的数组    
  122.     event *m_SignalEvents[MAX_SIGNAL];  //自定义的信号处理    
  123.   
  124. private:    
  125.     //初始化子线程的数据    
  126.     void SetupThread(LibeventThread *thread);    
  127.   
  128.     //子线程的入门函数    
  129.     static void *WorkerLibevent(void *arg);    
  130.     //(主线程收到请求后),对应子线程的处理函数    
  131.     static void ThreadProcess(int fd, short which, void *arg);    
  132.     //被libevent回调的各个静态函数    
  133.     static void ListenerEventCb(evconnlistener *listener, evutil_socket_t fd,    
  134.         sockaddr *sa, int socklen, void *user_data);    
  135.     static void ReadEventCb(struct bufferevent *bev, void *data);    
  136.     static void WriteEventCb(struct bufferevent *bev, void *data);     
  137.     static void CloseEventCb(struct bufferevent *bev, short events, void *data);    
  138.   
  139. protected:    
  140.     //这五个虚函数,一般是要被子类继承,并在其中处理具体业务的    
  141.   
  142.     //新建连接成功后,会调用该函数    
  143.     virtual void ConnectionEvent(Conn *conn) { }    
  144.   
  145.     //读取完数据后,会调用该函数    
  146.     virtual void ReadEvent(Conn *conn) { }    
  147.   
  148.     //发送完成功后,会调用该函数(因为串包的问题,所以并不是每次发送完数据都会被调用)    
  149.     virtual void WriteEvent(Conn *conn) { }    
  150.   
  151.     //断开连接(客户自动断开或异常断开)后,会调用该函数    
  152.     virtual void CloseEvent(Conn *conn, short events) { }    
  153.   
  154. public:    
  155.     MultiServer(int count);    
  156.     ~MultiServer();    
  157.   
  158.     //设置监听的端口号,如果不需要监听,请将其设置为EXIT_CODE    
  159.     void SetPort(int port)    
  160.     { m_Port = port; }    
  161.   
  162.     //开始事件循环    
  163.     bool StartRun();    
  164.     //在tv时间里结束事件循环    
  165.     //否tv为空,则立即停止    
  166.     void StopRun(timeval *tv);    
  167.   
  168.     //添加和删除信号处理事件    
  169.     //sig是信号,ptr为要回调的函数    
  170.     bool AddSignalEvent(int sig, void (*ptr)(int, short, void*));    
  171.     bool DeleteSignalEvent(int sig);    
  172.   
  173.     //添加和删除定时事件    
  174.     //ptr为要回调的函数,tv是间隔时间,once决定是否只执行一次    
  175.     event *AddTimerEvent(void(*ptr)(int, short, void*),    
  176.         timeval tv, bool once);    
  177.     bool DeleteTImerEvent(event *ev);    
  178. };    
  179.   
  180. #endif    

 

实现文件:MulitServer.cpp

  1. //MultiServer.cpp    
  2. #include "MultiServer.h"    
  3.   
  4. Conn::Conn(int fd) : m_fd(fd)    
  5. {    
  6.     m_Prev = NULL;    
  7.     m_Next = NULL;    
  8. }    
声明:该文观点仅代表作者本人,牛骨文系教育信息发布平台,牛骨文仅提供信息存储空间服务。