首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >对libevent+多线程服务器模型的C++封装类

对libevent+多线程服务器模型的C++封装类

作者头像
bear_fish
发布2018-09-20 15:22:39
1.9K0
发布2018-09-20 15:22:39
举报

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

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

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

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

上代码:

头文件:TcpEventServer.h

<span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//TcpEventServer.h</span>
<span class="preprocessor" style="color: rgb(153, 153, 153); font-weight: bold;">#ifndef TCPEVENTSERVER_H_</span>
<span class="preprocessor" style="color: rgb(153, 153, 153); font-weight: bold;">#define TCPEVENTSERVER_H_</span>

<span class="preprocessor" style="color: rgb(153, 153, 153); font-weight: bold;">#include <stdio.h></span>
<span class="preprocessor" style="color: rgb(153, 153, 153); font-weight: bold;">#include <stdlib.h></span>
<span class="preprocessor" style="color: rgb(153, 153, 153); font-weight: bold;">#include <unistd.h></span>
<span class="preprocessor" style="color: rgb(153, 153, 153); font-weight: bold;">#include <string.h></span>
<span class="preprocessor" style="color: rgb(153, 153, 153); font-weight: bold;">#include <errno.h></span>
<span class="preprocessor" style="color: rgb(153, 153, 153); font-weight: bold;">#include <signal.h></span>
<span class="preprocessor" style="color: rgb(153, 153, 153); font-weight: bold;">#include <time.h></span>
<span class="preprocessor" style="color: rgb(153, 153, 153); font-weight: bold;">#include <pthread.h></span>
<span class="preprocessor" style="color: rgb(153, 153, 153); font-weight: bold;">#include <fcntl.h></span>

<span class="preprocessor" style="color: rgb(153, 153, 153); font-weight: bold;">#include <map></span>
<span class="keyword" style="font-weight: bold;">using</span> <span class="built_in" style="color: rgb(0, 134, 179);">std</span>::<span class="built_in" style="color: rgb(0, 134, 179);">map</span>;

<span class="preprocessor" style="color: rgb(153, 153, 153); font-weight: bold;">#include <event.h></span>
<span class="preprocessor" style="color: rgb(153, 153, 153); font-weight: bold;">#include <event2/bufferevent.h></span>
<span class="preprocessor" style="color: rgb(153, 153, 153); font-weight: bold;">#include <event2/buffer.h></span>
<span class="preprocessor" style="color: rgb(153, 153, 153); font-weight: bold;">#include <event2/listener.h></span>
<span class="preprocessor" style="color: rgb(153, 153, 153); font-weight: bold;">#include <event2/util.h></span>
<span class="preprocessor" style="color: rgb(153, 153, 153); font-weight: bold;">#include <event2/event.h></span>

<span class="keyword" style="font-weight: bold;">class</span> TcpEventServer;
<span class="keyword" style="font-weight: bold;">class</span> Conn;
<span class="keyword" style="font-weight: bold;">class</span> ConnQueue;
<span class="keyword" style="font-weight: bold;">struct</span> LibeventThread;

<span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//这个类一个链表的结点类,结点里存储各个连接的信息,</span>
<span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//并提供了读写数据的接口</span>
<span class="keyword" style="font-weight: bold;">class</span> Conn
{
<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//此类只能由TcpBaseServer创建,</span>
<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//并由ConnQueue类管理</span>
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">friend</span> <span class="keyword" style="font-weight: bold;">class</span> ConnQueue;
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">friend</span> <span class="keyword" style="font-weight: bold;">class</span> TcpEventServer;

<span class="keyword" style="font-weight: bold;">private</span>:
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">const</span> <span class="keyword" style="font-weight: bold;">int</span> m_fd;				<span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//socket的ID</span>
<span class="indent">  </span>evbuffer *m_ReadBuf;		<span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//读数据的缓冲区</span>
<span class="indent">  </span>evbuffer *m_WriteBuf;		<span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//写数据的缓冲区</span>

<span class="indent">  </span>Conn *m_Prev;				<span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//前一个结点的指针</span>
<span class="indent">  </span>Conn *m_Next;				<span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//后一个结点的指针</span>
<span class="indent">  </span>LibeventThread *m_Thread;

<span class="indent">  </span>Conn(<span class="keyword" style="font-weight: bold;">int</span> fd=<span class="number" style="color: rgb(0, 153, 153);">0</span>);
<span class="indent">  </span>~Conn();

<span class="keyword" style="font-weight: bold;">public</span>:
<span class="indent">  </span>LibeventThread *GetThread() { <span class="keyword" style="font-weight: bold;">return</span> m_Thread; }
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">int</span> GetFd() { <span class="keyword" style="font-weight: bold;">return</span> m_fd; }

<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//获取可读数据的长度</span>
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">int</span> GetReadBufferLen()
<span class="indent">  </span>{ <span class="keyword" style="font-weight: bold;">return</span> evbuffer_get_length(m_ReadBuf); }

<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//从读缓冲区中取出len个字节的数据,存入buffer中,若不够,则读出所有数据</span>
<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//返回读出数据的字节数</span>
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">int</span> GetReadBuffer(<span class="keyword" style="font-weight: bold;">char</span> *buffer, <span class="keyword" style="font-weight: bold;">int</span> len)
<span class="indent">  </span>{ <span class="keyword" style="font-weight: bold;">return</span> evbuffer_remove(m_ReadBuf, buffer, len); }

<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//从读缓冲区中复制出len个字节的数据,存入buffer中,若不够,则复制出所有数据</span>
<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//返回复制出数据的字节数</span>
<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//执行该操作后,数据还会留在缓冲区中,buffer中的数据只是原数据的副本</span>
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">int</span> CopyReadBuffer(<span class="keyword" style="font-weight: bold;">char</span> *buffer, <span class="keyword" style="font-weight: bold;">int</span> len)
<span class="indent">  </span>{ <span class="keyword" style="font-weight: bold;">return</span> evbuffer_copyout(m_ReadBuf, buffer, len); }

<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//获取可写数据的长度</span>
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">int</span> GetWriteBufferLen()
<span class="indent">  </span>{ <span class="keyword" style="font-weight: bold;">return</span> evbuffer_get_length(m_WriteBuf); }

<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//将数据加入写缓冲区,准备发送</span>
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">int</span> AddToWriteBuffer(<span class="keyword" style="font-weight: bold;">char</span> *buffer, <span class="keyword" style="font-weight: bold;">int</span> len)
<span class="indent">  </span>{ <span class="keyword" style="font-weight: bold;">return</span> evbuffer_add(m_WriteBuf, buffer, len); }

<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//将读缓冲区中的数据移动到写缓冲区</span>
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">void</span> MoveBufferData()
<span class="indent">  </span>{ evbuffer_add_buffer(m_WriteBuf, m_ReadBuf); }

};

<span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//带头尾结点的双链表类,每个结点存储一个连接的数据</span>
<span class="keyword" style="font-weight: bold;">class</span> ConnQueue
{
<span class="keyword" style="font-weight: bold;">private</span>:
<span class="indent">  </span>Conn *m_head;
<span class="indent">  </span>Conn *m_tail;
<span class="keyword" style="font-weight: bold;">public</span>:
<span class="indent">  </span>ConnQueue();
<span class="indent">  </span>~ConnQueue();
<span class="indent">  </span>Conn *InsertConn(<span class="keyword" style="font-weight: bold;">int</span> fd, LibeventThread *t);
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">void</span> DeleteConn(Conn *c);
<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//void PrintQueue();</span>
};

<span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//每个子线程的线程信息</span>
<span class="keyword" style="font-weight: bold;">struct</span> LibeventThread
{
<span class="indent">  </span>pthread_t tid;				<span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//线程的ID</span>
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">struct</span> event_base *base;	<span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//libevent的事件处理机</span>
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">struct</span> event notifyEvent;	<span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//监听管理的事件机</span>
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">int</span> notifyReceiveFd;		<span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//管理的接收端</span>
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">int</span> notifySendFd;			<span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//管道的发送端</span>
<span class="indent">  </span>ConnQueue connectQueue;		<span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//socket连接的链表</span>

<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//在libevent的事件处理中要用到很多回调函数,不能使用类隐含的this指针</span>
<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//所以用这样方式将TcpBaseServer的类指针传过去</span>
<span class="indent">  </span>TcpEventServer *tcpConnect;	 <span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//TcpBaseServer类的指针</span>
};

<span class="keyword" style="font-weight: bold;">class</span> TcpEventServer
{
<span class="keyword" style="font-weight: bold;">private</span>:
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">int</span> m_ThreadCount;					<span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//子线程数</span>
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">int</span> m_Port;							<span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//监听的端口</span>
<span class="indent">  </span>LibeventThread *m_MainBase;			<span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//主线程的libevent事件处理机</span>
<span class="indent">  </span>LibeventThread *m_Threads;			<span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//存储各个子线程信息的数组</span>
<span class="indent">  </span><span class="stl_container"><span class="built_in" style="color: rgb(0, 134, 179);">map</span><<span class="keyword" style="font-weight: bold;">int</span>, event*></span> m_SignalEvents;	<span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//自定义的信号处理</span>

<span class="keyword" style="font-weight: bold;">public</span>:
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">static</span> <span class="keyword" style="font-weight: bold;">const</span> <span class="keyword" style="font-weight: bold;">int</span> EXIT_CODE = -<span class="number" style="color: rgb(0, 153, 153);">1</span>;

<span class="keyword" style="font-weight: bold;">private</span>:
<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//初始化子线程的数据</span>
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">void</span> SetupThread(LibeventThread *thread);

<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//子线程的入门函数</span>
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">static</span> <span class="keyword" style="font-weight: bold;">void</span> *WorkerLibevent(<span class="keyword" style="font-weight: bold;">void</span> *arg);
<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//(主线程收到请求后),对应子线程的处理函数</span>
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">static</span> <span class="keyword" style="font-weight: bold;">void</span> ThreadProcess(<span class="keyword" style="font-weight: bold;">int</span> fd, <span class="keyword" style="font-weight: bold;">short</span> which, <span class="keyword" style="font-weight: bold;">void</span> *arg);
<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//被libevent回调的各个静态函数</span>
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">static</span> <span class="keyword" style="font-weight: bold;">void</span> ListenerEventCb(evconnlistener *listener, evutil_socket_t fd,
<span class="indent">  </span><span class="indent">  </span>sockaddr *sa, <span class="keyword" style="font-weight: bold;">int</span> socklen, <span class="keyword" style="font-weight: bold;">void</span> *user_data);
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">static</span> <span class="keyword" style="font-weight: bold;">void</span> ReadEventCb(<span class="keyword" style="font-weight: bold;">struct</span> bufferevent *bev, <span class="keyword" style="font-weight: bold;">void</span> *data);
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">static</span> <span class="keyword" style="font-weight: bold;">void</span> WriteEventCb(<span class="keyword" style="font-weight: bold;">struct</span> bufferevent *bev, <span class="keyword" style="font-weight: bold;">void</span> *data); 
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">static</span> <span class="keyword" style="font-weight: bold;">void</span> CloseEventCb(<span class="keyword" style="font-weight: bold;">struct</span> bufferevent *bev, <span class="keyword" style="font-weight: bold;">short</span> events, <span class="keyword" style="font-weight: bold;">void</span> *data);

<span class="keyword" style="font-weight: bold;">protected</span>:
<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//这五个虚函数,一般是要被子类继承,并在其中处理具体业务的</span>

<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//新建连接成功后,会调用该函数</span>
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">virtual</span> <span class="keyword" style="font-weight: bold;">void</span> ConnectionEvent(Conn *conn) { }

<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//读取完数据后,会调用该函数</span>
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">virtual</span> <span class="keyword" style="font-weight: bold;">void</span> ReadEvent(Conn *conn) { }

<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//发送完成功后,会调用该函数(因为串包的问题,所以并不是每次发送完数据都会被调用)</span>
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">virtual</span> <span class="keyword" style="font-weight: bold;">void</span> WriteEvent(Conn *conn) { }

<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//断开连接(客户自动断开或异常断开)后,会调用该函数</span>
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">virtual</span> <span class="keyword" style="font-weight: bold;">void</span> CloseEvent(Conn *conn, <span class="keyword" style="font-weight: bold;">short</span> events) { }

<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//发生致命错误(如果创建子线程失败等)后,会调用该函数</span>
<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//该函数的默认操作是输出错误提示,终止程序</span>
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">virtual</span> <span class="keyword" style="font-weight: bold;">void</span> ErrorQuit(<span class="keyword" style="font-weight: bold;">const</span> <span class="keyword" style="font-weight: bold;">char</span> *str);

<span class="keyword" style="font-weight: bold;">public</span>:
<span class="indent">  </span>TcpEventServer(<span class="keyword" style="font-weight: bold;">int</span> count);
<span class="indent">  </span>~TcpEventServer();

<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//设置监听的端口号,如果不需要监听,请将其设置为EXIT_CODE</span>
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">void</span> SetPort(<span class="keyword" style="font-weight: bold;">int</span> port)
<span class="indent">  </span>{ m_Port = port; }

<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//开始事件循环</span>
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">bool</span> StartRun();
<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//在tv时间里结束事件循环</span>
<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//否tv为空,则立即停止</span>
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">void</span> StopRun(timeval *tv);

<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//添加和删除信号处理事件</span>
<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//sig是信号,ptr为要回调的函数</span>
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">bool</span> AddSignalEvent(<span class="keyword" style="font-weight: bold;">int</span> sig, <span class="keyword" style="font-weight: bold;">void</span> (*ptr)(<span class="keyword" style="font-weight: bold;">int</span>, <span class="keyword" style="font-weight: bold;">short</span>, <span class="keyword" style="font-weight: bold;">void</span>*));
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">bool</span> DeleteSignalEvent(<span class="keyword" style="font-weight: bold;">int</span> sig);

<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//添加和删除定时事件</span>
<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//ptr为要回调的函数,tv是间隔时间,once决定是否只执行一次</span>
<span class="indent">  </span>event *AddTimerEvent(<span class="keyword" style="font-weight: bold;">void</span>(*ptr)(<span class="keyword" style="font-weight: bold;">int</span>, <span class="keyword" style="font-weight: bold;">short</span>, <span class="keyword" style="font-weight: bold;">void</span>*),
<span class="indent">  </span><span class="indent">  </span>timeval tv, <span class="keyword" style="font-weight: bold;">bool</span> once);
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">bool</span> DeleteTImerEvent(event *ev);
};

<span class="preprocessor" style="color: rgb(153, 153, 153); font-weight: bold;">#endif</span>

实现文件:TcpEventServer.cpp

<span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//TcpEventServer.cpp</span>
<span class="preprocessor" style="color: rgb(153, 153, 153); font-weight: bold;">#include "TcpEventServer.h"</span>

Conn::Conn(<span class="keyword" style="font-weight: bold;">int</span> fd) : m_fd(fd)
{
<span class="indent">  </span>m_Prev = NULL;
<span class="indent">  </span>m_Next = NULL;
}

Conn::~Conn()
{

}

ConnQueue::ConnQueue()
{
<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//建立头尾结点,并调整其指针</span>
<span class="indent">  </span>m_head = <span class="keyword" style="font-weight: bold;">new</span> Conn(<span class="number" style="color: rgb(0, 153, 153);">0</span>);
<span class="indent">  </span>m_tail = <span class="keyword" style="font-weight: bold;">new</span> Conn(<span class="number" style="color: rgb(0, 153, 153);">0</span>);
<span class="indent">  </span>m_head->m_Prev = m_tail->m_Next = NULL;
<span class="indent">  </span>m_head->m_Next = m_tail;
<span class="indent">  </span>m_tail->m_Prev = m_head;
}

ConnQueue::~ConnQueue()
{
<span class="indent">  </span>Conn *tcur, *tnext;
<span class="indent">  </span>tcur = m_head;
<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//循环删除链表中的各个结点</span>
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">while</span>( tcur != NULL )
<span class="indent">  </span>{
<span class="indent">  </span><span class="indent">  </span>tnext = tcur->m_Next;
<span class="indent">  </span><span class="indent">  </span>delete tcur;
<span class="indent">  </span><span class="indent">  </span>tcur = tnext;
<span class="indent">  </span>}
}

Conn *ConnQueue::InsertConn(<span class="keyword" style="font-weight: bold;">int</span> fd, LibeventThread *t)
{
<span class="indent">  </span>Conn *c = <span class="keyword" style="font-weight: bold;">new</span> Conn(fd);
<span class="indent">  </span>c->m_Thread = t;
<span class="indent">  </span>Conn *next = m_head->m_Next;

<span class="indent">  </span>c->m_Prev = m_head;
<span class="indent">  </span>c->m_Next = m_head->m_Next;
<span class="indent">  </span>m_head->m_Next = c;
<span class="indent">  </span>next->m_Prev = c;
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">return</span> c;
}

<span class="keyword" style="font-weight: bold;">void</span> ConnQueue::DeleteConn(Conn *c)
{
<span class="indent">  </span>c->m_Prev->m_Next = c->m_Next;
<span class="indent">  </span>c->m_Next->m_Prev = c->m_Prev;
<span class="indent">  </span>delete c;
}

<span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">/*
void ConnQueue::PrintQueue()
{
<span class="indent">  </span>Conn *cur = m_head->m_Next;
<span class="indent">  </span>while( cur->m_Next != NULL )
<span class="indent">  </span>{
<span class="indent">  </span><span class="indent">  </span>printf("%d ", cur->m_fd);
<span class="indent">  </span><span class="indent">  </span>cur = cur->m_Next;
<span class="indent">  </span>}
<span class="indent">  </span>printf("\n");
}
*/</span>

TcpEventServer::TcpEventServer(<span class="keyword" style="font-weight: bold;">int</span> count)
{
<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//初始化各项数据</span>
<span class="indent">  </span>m_ThreadCount = count;
<span class="indent">  </span>m_Port = -<span class="number" style="color: rgb(0, 153, 153);">1</span>;
<span class="indent">  </span>m_MainBase = <span class="keyword" style="font-weight: bold;">new</span> LibeventThread;
<span class="indent">  </span>m_Threads = <span class="keyword" style="font-weight: bold;">new</span> LibeventThread[m_ThreadCount];
<span class="indent">  </span>m_MainBase->tid = pthread_self();
<span class="indent">  </span>m_MainBase-><span class="keyword" style="font-weight: bold;">base</span> = event_base_new();

<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//初始化各个子线程的结构体</span>
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">for</span>(<span class="keyword" style="font-weight: bold;">int</span> i=<span class="number" style="color: rgb(0, 153, 153);">0</span>; i<m_ThreadCount; i++)
<span class="indent">  </span>{
<span class="indent">  </span><span class="indent">  </span>SetupThread(&m_Threads[i]);
<span class="indent">  </span>}

}

TcpEventServer::~TcpEventServer()
{
<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//停止事件循环(如果事件循环没开始,则没效果)</span>
<span class="indent">  </span>StopRun(NULL);

<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//释放内存</span>
<span class="indent">  </span>event_base_free(m_MainBase-><span class="keyword" style="font-weight: bold;">base</span>);
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">for</span>(<span class="keyword" style="font-weight: bold;">int</span> i=<span class="number" style="color: rgb(0, 153, 153);">0</span>; i<m_ThreadCount; i++)
<span class="indent">  </span><span class="indent">  </span>event_base_free(m_Threads[i].<span class="keyword" style="font-weight: bold;">base</span>);

<span class="indent">  </span>delete m_MainBase;
<span class="indent">  </span>delete [] m_Threads;
}

<span class="keyword" style="font-weight: bold;">void</span> TcpEventServer::ErrorQuit(<span class="keyword" style="font-weight: bold;">const</span> <span class="keyword" style="font-weight: bold;">char</span> *str)
{
<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//输出错误信息,退出程序</span>
<span class="indent">  </span>fprintf(stderr, <span class="string" style="color: rgb(221, 17, 68);">"%s"</span>, str);   
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">if</span>( errno != <span class="number" style="color: rgb(0, 153, 153);">0</span> )    
<span class="indent">  </span><span class="indent">  </span>fprintf(stderr, <span class="string" style="color: rgb(221, 17, 68);">" : %s"</span>, strerror(errno));    
<span class="indent">  </span>fprintf(stderr, <span class="string" style="color: rgb(221, 17, 68);">"\n"</span>);        
<span class="indent">  </span>exit(<span class="number" style="color: rgb(0, 153, 153);">1</span>);    
}

<span class="keyword" style="font-weight: bold;">void</span> TcpEventServer::SetupThread(LibeventThread *me)
{
<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//建立libevent事件处理机制</span>
<span class="indent">  </span>me->tcpConnect = <span class="keyword" style="font-weight: bold;">this</span>;
<span class="indent">  </span>me-><span class="keyword" style="font-weight: bold;">base</span> = event_base_new();
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">if</span>( NULL == me-><span class="keyword" style="font-weight: bold;">base</span> )
<span class="indent">  </span><span class="indent">  </span>ErrorQuit(<span class="string" style="color: rgb(221, 17, 68);">"event base new error"</span>);

<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//在主线程和子线程之间建立管道</span>
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">int</span> fds[<span class="number" style="color: rgb(0, 153, 153);">2</span>];
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">if</span>( pipe(fds) )
<span class="indent">  </span><span class="indent">  </span>ErrorQuit(<span class="string" style="color: rgb(221, 17, 68);">"create pipe error"</span>);
<span class="indent">  </span>me->notifyReceiveFd = fds[<span class="number" style="color: rgb(0, 153, 153);">0</span>];
<span class="indent">  </span>me->notifySendFd = fds[<span class="number" style="color: rgb(0, 153, 153);">1</span>];

<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//让子线程的状态机监听管道</span>
<span class="indent">  </span>event_set( &me->notifyEvent, me->notifyReceiveFd,
<span class="indent">  </span><span class="indent">  </span>EV_READ | EV_PERSIST, ThreadProcess, me );
<span class="indent">  </span>event_base_set(me-><span class="keyword" style="font-weight: bold;">base</span>, &me->notifyEvent);
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">if</span> ( event_add(&me->notifyEvent, <span class="number" style="color: rgb(0, 153, 153);">0</span>) == -<span class="number" style="color: rgb(0, 153, 153);">1</span> )
<span class="indent">  </span><span class="indent">  </span>ErrorQuit(<span class="string" style="color: rgb(221, 17, 68);">"Can't monitor libevent notify pipe\n"</span>);
}

<span class="keyword" style="font-weight: bold;">void</span> *TcpEventServer::WorkerLibevent(<span class="keyword" style="font-weight: bold;">void</span> *arg)
{
<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//开启libevent的事件循环,准备处理业务</span>
<span class="indent">  </span>LibeventThread *me = (LibeventThread*)arg;
<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//printf("thread %u started\n", (unsigned int)me->tid);</span>
<span class="indent">  </span>event_base_dispatch(me-><span class="keyword" style="font-weight: bold;">base</span>);
<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//printf("subthread done\n");</span>
}

<span class="keyword" style="font-weight: bold;">bool</span> TcpEventServer::StartRun()
{
<span class="indent">  </span>evconnlistener *listener;

<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//如果端口号不是EXIT_CODE,就监听该端口号</span>
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">if</span>( m_Port != EXIT_CODE )
<span class="indent">  </span>{
<span class="indent">  </span><span class="indent">  </span>sockaddr_in sin;
<span class="indent">  </span><span class="indent">  </span>memset(&sin, <span class="number" style="color: rgb(0, 153, 153);">0</span>, <span class="keyword" style="font-weight: bold;">sizeof</span>(sin));
<span class="indent">  </span><span class="indent">  </span>sin.sin_family = AF_INET;
<span class="indent">  </span><span class="indent">  </span>sin.sin_port = htons(m_Port);
<span class="indent">  </span><span class="indent">  </span>listener = evconnlistener_new_bind(m_MainBase-><span class="keyword" style="font-weight: bold;">base</span>, 
<span class="indent">  </span><span class="indent">  </span><span class="indent">  </span>ListenerEventCb, (<span class="keyword" style="font-weight: bold;">void</span>*)<span class="keyword" style="font-weight: bold;">this</span>,
<span class="indent">  </span><span class="indent">  </span><span class="indent">  </span>LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, -<span class="number" style="color: rgb(0, 153, 153);">1</span>,
<span class="indent">  </span><span class="indent">  </span><span class="indent">  </span>(sockaddr*)&sin, <span class="keyword" style="font-weight: bold;">sizeof</span>(sockaddr_in));
<span class="indent">  </span><span class="indent">  </span><span class="keyword" style="font-weight: bold;">if</span>( NULL == listener )
<span class="indent">  </span><span class="indent">  </span><span class="indent">  </span>ErrorQuit(<span class="string" style="color: rgb(221, 17, 68);">"TCP listen error"</span>);
<span class="indent">  </span>}

<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//开启各个子线程</span>
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">for</span>(<span class="keyword" style="font-weight: bold;">int</span> i=<span class="number" style="color: rgb(0, 153, 153);">0</span>; i<m_ThreadCount; i++)
<span class="indent">  </span>{
<span class="indent">  </span><span class="indent">  </span>pthread_create(&m_Threads[i].tid, NULL,  
<span class="indent">  </span><span class="indent">  </span><span class="indent">  </span>WorkerLibevent, (<span class="keyword" style="font-weight: bold;">void</span>*)&m_Threads[i]);
<span class="indent">  </span>}

<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//开启主线程的事件循环</span>
<span class="indent">  </span>event_base_dispatch(m_MainBase-><span class="keyword" style="font-weight: bold;">base</span>);

<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//事件循环结果,释放监听者的内存</span>
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">if</span>( m_Port != EXIT_CODE )
<span class="indent">  </span>{
<span class="indent">  </span><span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//printf("free listen\n");</span>
<span class="indent">  </span><span class="indent">  </span>evconnlistener_free(listener);
<span class="indent">  </span>}
}

<span class="keyword" style="font-weight: bold;">void</span> TcpEventServer::StopRun(timeval *tv)
{
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">int</span> contant = EXIT_CODE;
<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//向各个子线程的管理中写入EXIT_CODE,通知它们退出</span>
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">for</span>(<span class="keyword" style="font-weight: bold;">int</span> i=<span class="number" style="color: rgb(0, 153, 153);">0</span>; i<m_ThreadCount; i++)
<span class="indent">  </span>{
<span class="indent">  </span><span class="indent">  </span>write(m_Threads[i].notifySendFd, &contant, <span class="keyword" style="font-weight: bold;">sizeof</span>(<span class="keyword" style="font-weight: bold;">int</span>));
<span class="indent">  </span>}
<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//结果主线程的事件循环</span>
<span class="indent">  </span>event_base_loopexit(m_MainBase-><span class="keyword" style="font-weight: bold;">base</span>, tv);
}

<span class="keyword" style="font-weight: bold;">void</span> TcpEventServer::ListenerEventCb(<span class="keyword" style="font-weight: bold;">struct</span> evconnlistener *listener, 
<span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="indent">  </span>evutil_socket_t fd,
<span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="keyword" style="font-weight: bold;">struct</span> sockaddr *sa, 
<span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="keyword" style="font-weight: bold;">int</span> socklen, 
<span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="keyword" style="font-weight: bold;">void</span> *user_data)
{
<span class="indent">  </span>TcpEventServer *server = (TcpEventServer*)user_data;

<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//随机选择一个子线程,通过管道向其传递socket描述符</span>
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">int</span> num = rand() % server->m_ThreadCount;
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">int</span> sendfd = server->m_Threads[num].notifySendFd;
<span class="indent">  </span>write(sendfd, &fd, <span class="keyword" style="font-weight: bold;">sizeof</span>(evutil_socket_t));
}

<span class="keyword" style="font-weight: bold;">void</span> TcpEventServer::ThreadProcess(<span class="keyword" style="font-weight: bold;">int</span> fd, <span class="keyword" style="font-weight: bold;">short</span> which, <span class="keyword" style="font-weight: bold;">void</span> *arg)
{
<span class="indent">  </span>LibeventThread *me = (LibeventThread*)arg;

<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//从管道中读取数据(socket的描述符或操作码)</span>
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">int</span> pipefd = me->notifyReceiveFd;
<span class="indent">  </span>evutil_socket_t confd;
<span class="indent">  </span>read(pipefd, &confd, <span class="keyword" style="font-weight: bold;">sizeof</span>(evutil_socket_t));

<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//如果操作码是EXIT_CODE,则终于事件循环</span>
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">if</span>( EXIT_CODE == confd )
<span class="indent">  </span>{
<span class="indent">  </span><span class="indent">  </span>event_base_loopbreak(me-><span class="keyword" style="font-weight: bold;">base</span>);
<span class="indent">  </span><span class="indent">  </span><span class="keyword" style="font-weight: bold;">return</span>;
<span class="indent">  </span>}

<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//新建连接</span>
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">struct</span> bufferevent *bev;
<span class="indent">  </span>bev = bufferevent_socket_new(me-><span class="keyword" style="font-weight: bold;">base</span>, confd, BEV_OPT_CLOSE_ON_FREE);
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">if</span> (!bev)
<span class="indent">  </span>{
<span class="indent">  </span><span class="indent">  </span>fprintf(stderr, <span class="string" style="color: rgb(221, 17, 68);">"Error constructing bufferevent!"</span>);
<span class="indent">  </span><span class="indent">  </span>event_base_loopbreak(me-><span class="keyword" style="font-weight: bold;">base</span>);
<span class="indent">  </span><span class="indent">  </span><span class="keyword" style="font-weight: bold;">return</span>;
<span class="indent">  </span>}

<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//将该链接放入队列</span>
<span class="indent">  </span>Conn *conn = me->connectQueue.InsertConn(confd, me);

<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//准备从socket中读写数据</span>
<span class="indent">  </span>bufferevent_setcb(bev, ReadEventCb, WriteEventCb, CloseEventCb, conn);
<span class="indent">  </span>bufferevent_enable(bev, EV_WRITE);
<span class="indent">  </span>bufferevent_enable(bev, EV_READ);

<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//调用用户自定义的连接事件处理函数</span>
<span class="indent">  </span>me->tcpConnect->ConnectionEvent(conn);
}

<span class="keyword" style="font-weight: bold;">void</span> TcpEventServer::ReadEventCb(<span class="keyword" style="font-weight: bold;">struct</span> bufferevent *bev, <span class="keyword" style="font-weight: bold;">void</span> *data)
{
<span class="indent">  </span>Conn *conn = (Conn*)data;
<span class="indent">  </span>conn->m_ReadBuf = bufferevent_get_input(bev);
<span class="indent">  </span>conn->m_WriteBuf = bufferevent_get_output(bev);

<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//调用用户自定义的读取事件处理函数</span>
<span class="indent">  </span>conn->m_Thread->tcpConnect->ReadEvent(conn);
} 

<span class="keyword" style="font-weight: bold;">void</span> TcpEventServer::WriteEventCb(<span class="keyword" style="font-weight: bold;">struct</span> bufferevent *bev, <span class="keyword" style="font-weight: bold;">void</span> *data)
{
<span class="indent">  </span>Conn *conn = (Conn*)data;
<span class="indent">  </span>conn->m_ReadBuf = bufferevent_get_input(bev);
<span class="indent">  </span>conn->m_WriteBuf = bufferevent_get_output(bev);

<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//调用用户自定义的写入事件处理函数</span>
<span class="indent">  </span>conn->m_Thread->tcpConnect->WriteEvent(conn);

}

<span class="keyword" style="font-weight: bold;">void</span> TcpEventServer::CloseEventCb(<span class="keyword" style="font-weight: bold;">struct</span> bufferevent *bev, <span class="keyword" style="font-weight: bold;">short</span> events, <span class="keyword" style="font-weight: bold;">void</span> *data)
{
<span class="indent">  </span>Conn *conn = (Conn*)data;
<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//调用用户自定义的断开事件处理函数</span>
<span class="indent">  </span>conn->m_Thread->tcpConnect->CloseEvent(conn, events);
<span class="indent">  </span>conn->GetThread()->connectQueue.DeleteConn(conn);
<span class="indent">  </span>bufferevent_free(bev);
}

<span class="keyword" style="font-weight: bold;">bool</span> TcpEventServer::AddSignalEvent(<span class="keyword" style="font-weight: bold;">int</span> sig, <span class="keyword" style="font-weight: bold;">void</span> (*ptr)(<span class="keyword" style="font-weight: bold;">int</span>, <span class="keyword" style="font-weight: bold;">short</span>, <span class="keyword" style="font-weight: bold;">void</span>*))
{
<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//新建一个信号事件</span>
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">event</span> *ev = evsignal_new(m_MainBase-><span class="keyword" style="font-weight: bold;">base</span>, sig, ptr, (<span class="keyword" style="font-weight: bold;">void</span>*)<span class="keyword" style="font-weight: bold;">this</span>);
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">if</span> ( !ev || 
<span class="indent">  </span><span class="indent">  </span>event_add(ev, NULL) < <span class="number" style="color: rgb(0, 153, 153);">0</span> )
<span class="indent">  </span>{
<span class="indent">  </span><span class="indent">  </span>event_del(ev);
<span class="indent">  </span><span class="indent">  </span><span class="keyword" style="font-weight: bold;">return</span> <span class="keyword" style="font-weight: bold;">false</span>;
<span class="indent">  </span>}

<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//删除旧的信号事件(同一个信号只能有一个信号事件)</span>
<span class="indent">  </span>DeleteSignalEvent(sig);
<span class="indent">  </span>m_SignalEvents[sig] = ev;

<span class="indent">  </span><span class="keyword" style="font-weight: bold;">return</span> <span class="keyword" style="font-weight: bold;">true</span>;
}

<span class="keyword" style="font-weight: bold;">bool</span> TcpEventServer::DeleteSignalEvent(<span class="keyword" style="font-weight: bold;">int</span> sig)
{
<span class="indent">  </span>map<<span class="keyword" style="font-weight: bold;">int</span>, <span class="keyword" style="font-weight: bold;">event</span>*>::iterator iter = m_SignalEvents.find(sig);
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">if</span>( iter == m_SignalEvents.end() )
<span class="indent">  </span><span class="indent">  </span><span class="keyword" style="font-weight: bold;">return</span> <span class="keyword" style="font-weight: bold;">false</span>;

<span class="indent">  </span>event_del(iter->second);
<span class="indent">  </span>m_SignalEvents.erase(iter);
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">return</span> <span class="keyword" style="font-weight: bold;">true</span>;
}

<span class="keyword" style="font-weight: bold;">event</span> *TcpEventServer::AddTimerEvent(<span class="keyword" style="font-weight: bold;">void</span> (*ptr)(<span class="keyword" style="font-weight: bold;">int</span>, <span class="keyword" style="font-weight: bold;">short</span>, <span class="keyword" style="font-weight: bold;">void</span> *), 
<span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="indent">  </span>  timeval tv, <span class="keyword" style="font-weight: bold;">bool</span> once)
{
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">int</span> flag = <span class="number" style="color: rgb(0, 153, 153);">0</span>;
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">if</span>( !once )
<span class="indent">  </span><span class="indent">  </span>flag = EV_PERSIST;

<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//新建定时器信号事件</span>
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">event</span> *ev = <span class="keyword" style="font-weight: bold;">new</span> <span class="keyword" style="font-weight: bold;">event</span>;
<span class="indent">  </span>event_assign(ev, m_MainBase-><span class="keyword" style="font-weight: bold;">base</span>, -<span class="number" style="color: rgb(0, 153, 153);">1</span>, flag, ptr, (<span class="keyword" style="font-weight: bold;">void</span>*)<span class="keyword" style="font-weight: bold;">this</span>);
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">if</span>( event_add(ev, &tv) < <span class="number" style="color: rgb(0, 153, 153);">0</span> )
<span class="indent">  </span>{
<span class="indent">  </span><span class="indent">  </span>event_del(ev);
<span class="indent">  </span><span class="indent">  </span><span class="keyword" style="font-weight: bold;">return</span> NULL;
<span class="indent">  </span>}
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">return</span> ev;
}

<span class="keyword" style="font-weight: bold;">bool</span> TcpEventServer::DeleteTImerEvent(<span class="keyword" style="font-weight: bold;">event</span> *ev)
{
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">int</span> res = event_del(ev);
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">return</span> (<span class="number" style="color: rgb(0, 153, 153);">0</span> == res);
}

测试文件:test.cpp

<span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">/*
这是一个测试用的服务器,只有两个功能:
1:对于每个已连接客户端,每10秒向其发送一句hello, world
2:若客户端向服务器发送数据,服务器收到后,再将数据回发给客户端
*/</span>
<span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//test.cpp</span>
<span class="preprocessor" style="color: rgb(153, 153, 153); font-weight: bold;">#include "TcpEventServer.h"</span>
<span class="preprocessor" style="color: rgb(153, 153, 153); font-weight: bold;">#include <set></span>
<span class="preprocessor" style="color: rgb(153, 153, 153); font-weight: bold;">#include <vector></span>
<span class="keyword" style="font-weight: bold;">using</span> <span class="keyword" style="font-weight: bold;">namespace</span> <span class="built_in" style="color: rgb(0, 134, 179);">std</span>;

<span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//测试示例</span>
<span class="keyword" style="font-weight: bold;">class</span> TestServer : <span class="keyword" style="font-weight: bold;">public</span> TcpEventServer
{
<span class="keyword" style="font-weight: bold;">private</span>:
<span class="indent">  </span><span class="stl_container"><span class="built_in" style="color: rgb(0, 134, 179);">vector</span><Conn*></span> vec;
<span class="keyword" style="font-weight: bold;">protected</span>:
<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//重载各个处理业务的虚函数</span>
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">void</span> ReadEvent(Conn *conn);
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">void</span> WriteEvent(Conn *conn);
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">void</span> ConnectionEvent(Conn *conn);
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">void</span> CloseEvent(Conn *conn, <span class="keyword" style="font-weight: bold;">short</span> events);
<span class="keyword" style="font-weight: bold;">public</span>:
<span class="indent">  </span>TestServer(<span class="keyword" style="font-weight: bold;">int</span> count) : TcpEventServer(count) { }
<span class="indent">  </span>~TestServer() { } 
<span class="indent">  </span>
<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//退出事件,响应Ctrl+C</span>
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">static</span> <span class="keyword" style="font-weight: bold;">void</span> QuitCb(<span class="keyword" style="font-weight: bold;">int</span> sig, <span class="keyword" style="font-weight: bold;">short</span> events, <span class="keyword" style="font-weight: bold;">void</span> *data);
<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">//定时器事件,每10秒向所有客户端发一句hello, world</span>
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">static</span> <span class="keyword" style="font-weight: bold;">void</span> TimeOutCb(<span class="keyword" style="font-weight: bold;">int</span> id, <span class="keyword" style="font-weight: bold;">int</span> <span class="keyword" style="font-weight: bold;">short</span> events, <span class="keyword" style="font-weight: bold;">void</span> *data);
};

<span class="keyword" style="font-weight: bold;">void</span> TestServer::ReadEvent(Conn *conn)
{
<span class="indent">  </span>conn->MoveBufferData();
}

<span class="keyword" style="font-weight: bold;">void</span> TestServer::WriteEvent(Conn *conn)
{

}

<span class="keyword" style="font-weight: bold;">void</span> TestServer::ConnectionEvent(Conn *conn)
{
<span class="indent">  </span>TestServer *me = (TestServer*)conn->GetThread()->tcpConnect;
<span class="indent">  </span>printf(<span class="string" style="color: rgb(221, 17, 68);">"new connection: %d\n"</span>, conn->GetFd());
<span class="indent">  </span>me->vec.push_back(conn);
}

<span class="keyword" style="font-weight: bold;">void</span> TestServer::CloseEvent(Conn *conn, <span class="keyword" style="font-weight: bold;">short</span> events)
{
<span class="indent">  </span>printf(<span class="string" style="color: rgb(221, 17, 68);">"connection closed: %d\n"</span>, conn->GetFd());
}

<span class="keyword" style="font-weight: bold;">void</span> TestServer::QuitCb(<span class="keyword" style="font-weight: bold;">int</span> sig, <span class="keyword" style="font-weight: bold;">short</span> events, <span class="keyword" style="font-weight: bold;">void</span> *data)
{ 
<span class="indent">  </span>printf(<span class="string" style="color: rgb(221, 17, 68);">"Catch the SIGINT signal, quit in one second\n"</span>);
<span class="indent">  </span>TestServer *me = (TestServer*)data;
<span class="indent">  </span>timeval tv = {<span class="number" style="color: rgb(0, 153, 153);">1</span>, <span class="number" style="color: rgb(0, 153, 153);">0</span>};
<span class="indent">  </span>me->StopRun(&tv);
}

<span class="keyword" style="font-weight: bold;">void</span> TestServer::TimeOutCb(<span class="keyword" style="font-weight: bold;">int</span> id, <span class="keyword" style="font-weight: bold;">short</span> events, <span class="keyword" style="font-weight: bold;">void</span> *data)
{
<span class="indent">  </span>TestServer *me = (TestServer*)data;
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">char</span> temp[<span class="number" style="color: rgb(0, 153, 153);">33</span>] = <span class="string" style="color: rgb(221, 17, 68);">"hello, world\n"</span>;
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">for</span>(<span class="keyword" style="font-weight: bold;">int</span> i=<span class="number" style="color: rgb(0, 153, 153);">0</span>; i<me->vec.size(); i++)
<span class="indent">  </span><span class="indent">  </span>me->vec[i]->AddToWriteBuffer(temp, strlen(temp));
}

<span class="keyword" style="font-weight: bold;">int</span> main()
{
<span class="indent">  </span>printf(<span class="string" style="color: rgb(221, 17, 68);">"pid: %d\n"</span>, getpid());
<span class="indent">  </span>TestServer server(<span class="number" style="color: rgb(0, 153, 153);">3</span>);
<span class="indent">  </span>server.AddSignalEvent(SIGINT, TestServer::QuitCb);
<span class="indent">  </span>timeval tv = {<span class="number" style="color: rgb(0, 153, 153);">10</span>, <span class="number" style="color: rgb(0, 153, 153);">0</span>};
<span class="indent">  </span>server.AddTimerEvent(TestServer::TimeOutCb, tv, <span class="keyword" style="font-weight: bold;">false</span>);
<span class="indent">  </span>server.SetPort(<span class="number" style="color: rgb(0, 153, 153);">2111</span>);
<span class="indent">  </span>server.StartRun();
<span class="indent">  </span>printf(<span class="string" style="color: rgb(221, 17, 68);">"done\n"</span>);
<span class="indent">  </span>
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">return</span> <span class="number" style="color: rgb(0, 153, 153);">0</span>;
}

编译与运行命令:

qch<span class="annotation">@LinuxMint</span> ~/program/ztemp $ g++ TcpEventServer.cpp test.cpp -o test -levent
qch<span class="annotation">@LinuxMint</span> ~/program/ztemp $ ./test
pid: <span class="number" style="color: rgb(0, 153, 153);">20264</span>
<span class="keyword" style="font-weight: bold;">new</span> connection: <span class="number" style="color: rgb(0, 153, 153);">22</span>
connection closed: <span class="number" style="color: rgb(0, 153, 153);">22</span>
^CCatch the SIGINT signal, quit in one second
done
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2015年04月05日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档