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

最近在看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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java编程技术

使用数据库悲观锁实现不可重入的分布式锁

在同一个jvm进程中时,可以使用JUC提供的一些锁来解决多个线程竞争同一个共享资源时候的线程安全问题,但是当多个不同机器上的不同jvm进程共同竞争同一个共享资源...

8310
来自专栏腾讯云API

腾讯云API:用Python使用腾讯云API(机器翻译实例)

腾讯云API地址:https://cloud.tencent.com/document/api

1.9K20
来自专栏木木玲

Netty 源码解析 ——— 服务端启动流程 (上)

30360
来自专栏me的随笔

ASP.NET MVC5中的Model验证

Model验证是ASP.NET MVC中的重要部分,它主要用于判断输入的数据类型及值是否符合我们设定的规则,这篇文章就介绍下ASP.NET MVC中Model验...

17420
来自专栏MasiMaro 的技术博文

Vista 及后续版本的新线程池

在上一篇的博文中,说了下老版本的线程池,在Vista之后,微软重新设计了一套线程池机制,并引入一组新的线程池API,新版线程池相对于老版本的来说,它的可控性更高...

17130
来自专栏黑泽君的专栏

day18_文件的上传和下载学习笔记

作用:告知服务器请求正文的MIME类型(文件类型)。(与请求消息头中:Content-Type作用是一致的) 可选值:

10210
来自专栏JadePeng的技术博客

Angular快速学习笔记(4) -- Observable与RxJS

93720
来自专栏技术记录

JAVA图片批量上传JS-带预览功能

这篇文章就简单的介绍一个很好用的文件上传工具,批量带预览功能。直接贴代码吧,都有注释,很好理解。 HTML页面 <!DOCTYPE html> <%@ tagl...

67250
来自专栏java 成神之路

RocketMQ 底层通信机制 源码分析

RocketMQ 底层通讯是使用Netty来实现的。 下面我们通过源码分析下RocketMQ是怎么利用Netty进行通讯的。

18420
来自专栏不会写文章的程序员不是好厨师

为Hibiscus写文之定时器篇——HashedWheelTimer

去年一年在简书大约写了25篇,在公司内网写了5篇博客。今年定个小目标吧,在简书产出高质量的博客50篇,加油!

21420

扫码关注云+社区

领取腾讯云代金券