学习github上的项目 flamingo 自己的笔记。
flamingo原作者的csdn是: analogous_love
flamingo是多线程的,但是本人能力有限,只是单线程的还算能理解一点。
自己参照flamingo实现的基于epoll的单线程服务端,git地址:https://gitee.com/storm_zy/StServerFrame
实现了简单的echo功能,很多代码直接拷贝自flamingo。
一、自己当时的想法
- 自己当时在想如何设计一个通用的Tcp服务端,就是将各个部分模块化,然后只需更改其中的少部分模块(例如业务session)即可
- 完成完整的服务端。然后就从网上找啊找,最后找到了一个大牛写的一个即时通信类软件 flamingo,从github上一搜就能搜到,然后开始研究其服务端框架。
- 然后终于感觉对框架稍微懂了一点点,所以就赶紧记录下来。
二、思考服务端框架的过程
-
实现一个通用的tcp服务端框架,首先要考虑的是网络通信,说白了就是系统提供的socket等一系列网络层的api的使用。
-
由此选用了效率比较高的通知机制 (Linux 下的 epoll 和 Windows 下的 iocp)。因为对iocp还没有研究,所以就用了epoll。
-
以上实现的功能可以用 事件模块 来称呼。就命名为 EventModule 吧。EventModule 应该包含的功能:
1> EventModule在这里就是对 socket 的事件监听,socket下面就用 fd 代替了。 2> 为fd添加事件 对应 EPOLL_CTL_ADD 3> 为fd更改事件 对应 EPOLL_CTL_MOD 4> 为fd删除事件 对应 EPOLL_CTL_DEL 5> 对外提供获取当前关注的活动的fd及其事件。
-
考虑通用性,增加一个 Channel 对 fd与EventModule之间的交互封装到 Channel中,这样对EventModule来说,只需对接 Channel即可,Channel再对接具体的
关注fd的对象(TcpConnection之类)。Channel应该包含的功能:1> 关注一个fd。一般关注的fd事件就是读事件和写事件。 2> 为fd增加读事件。 3> 为fd增加写事件。 4> 为fd删除读事件。 5> 为fd删除写事件。 6> 对外提供事件处理接口。
-
有了 EventModule 和 Channel之后,发现还需要一样东西,一样可以执行 EventModule的事件循环的东西,而且需要他来将 EventModule和Channel关联起来。这个东西就叫 EventLoop。
1> 持有一个EventModule的对象。 2> Channel可以通过EventLoop将自己关注的fd的事件添加、删除、更改到 EventModule。 3> 执行 EventModule的事件探测(epoll_wait),获取活动的fd的Channel,调用Channel的事件处理函数。
三、三个类对应的伪码实现
EventModule
class CChannel;
class CEventModule
{
public:
typedef std::vector<CChannel *> ChannelVec;
CEventModule() : m_hasInit(false) {}
~CEventModule() {}
public:
virtual int UpdateChannel(CChannel *channel) = 0; // 将channel关注的fd添加、修改事件
virtual void RemoveChannel(CChannel *channel) = 0; // 删除channel关注的fd的事件
// 对外(EventLoop)提供的获取当前活动的 fd的Channel的函数。
virtual int GetActiveChannels(int timeoutMs, ChannelVec& retActiveChannels) = 0;
virtual bool HasChannel(CChannel *channel) = 0;
virtual int Init() { m_hasInit = true; }
virtual bool HasInit() { return m_hasInit; }
protected:
bool m_hasInit;
};
- UpdateChannel 中判断 channel的状态 是否添加过,添加过则修改事件,没有添加过则是添加事件。事件可以直接通过接口 channel->events();获取。然后通过 epoll_ctl(…);将channel->fd();的事件添加到epoll。
- RemoveChannel就是将channel->fd(); 通过epoll_ctl(…,EPOLL_CTL_DEL,…)从epoll关注列表中删掉。
- GetActiveChannels就是执行 epoll_wait(…); 并将当前有事件的fd的channel放到retActiveChannels返回。
Channel
class CEventLoop;
class CChannel
{
public:
typedef std::function<void()> EventCallback;
CChannel(CEventLoop *loop, int fd);
~CChannel();
public:
void handleEvents();
bool update();
public:
void setReadCallBack(const EventCallback& cb) { readCallBack_ = cb; }
void setWriteCallBack(const EventCallback& cb) { writeCallBack_ = cb; }
void setcloseCallBack(const EventCallback& cb) { closeCallBack_ = cb; }
void setErrorCallBack(const EventCallback& cb) { errorCallBack_ = cb; }
void setEvents(int events) { m_events = events; }
void setRevents(int revents) { m_revents = revents; }
void setState(int state) { m_state = state; }
int events(void) { return m_events; }
int revents(void) { return m_revents; }
int fd(void) { return m_fd; }
int state(void) { return m_state; }
public:
bool enableReading();
bool disableReading();
bool enableWriting();
bool disableWriting();
bool hasWriteEvent();
void removeAllEvent();
private:
EventCallback readCallBack_;
EventCallback writeCallBack_;
EventCallback closeCallBack_;
EventCallback errorCallBack_;
private:
static const int kNoneEvent;
static const int kReadEvent;
static const int kWriteEvent;
const int m_fd;
int m_events; // 本channel关注的事件
int m_revents; // 当前从 EventModule 收到的等待处理的事件 recved events
CEventLoop *m_loop;
int m_state; // -1 没有被加入到 CEventModule 中, 1 已经加上了 2 已经删除了
};
- enable/disable那几个函数是从EventModule添加/删除对应的事件。
- handleEvents 是对外(EventLoop)提供的事件处理函数。
- 几个CallBack函数是对应 更上层的事件处理函数。会在handleEvents中根据收到不同的事件进行相应调用。
EventLoop
class CEventLoop;
class CChannel;
class CEventModule;
class CEventLoop
{
public:
CEventLoop();
~CEventLoop();
public:
bool updateChannel(CChannel * channel);
void removeChannel(CChannel * channel);
void loop();
void stop();
public:
void setEventModule(CEventModule *eventModule) { m_eventModule = eventModule; }
private:
typedef std::vector<CChannel *> ChannelVec;
CEventModule *m_eventModule;
ChannelVec m_activeChannels;
CChannel *m_curActiveChannel;
bool m_stoped;
};
- loop函数是调用 EventModule->GetActiveChannels(),并将返回的有事件的channel列表遍历,并调用channel->handelEvent()进行事件处理。
- updateChannel/removeChannel是提供给 Channel进行增加/删除事件的接口。
四、示例 伪码
int main()
{
CEventLoop mainLoop;
// 假设自己实现了一个 继承自 CEventModule 的事件模块类 CEpollModule
CEventModule *emd = new CEpollModule(&mainLoop); // 创建事件模块
emd->Init();
mainLoop.setEventModule(emd); // 将事件模块添加到 EventLoop
int fd = 创建一个socket;
CChannel *c = new CChannel(&mainLoop, fd); // 实际应该是 TcpConnection 关联一个 Channel
c->enableWrite(); // 将fd事件添加到EventModule
mainLoop.loop(); // 开始事件循环
return 0;
}
*以上只是伪码,示例大概怎样使用。
欢迎关注 [懒人漫说] 公众号,分享Java、Android、C/C++ 技术,包括基础、自己遇到的问题解决过程。
当然如果关注并留言问题的话,我们力所能及的话会帮你解决并回复哟。我们和你一样,是正在成长的程序员,我们也会分享自己的成长路上的感想,希望可以和你一起努力成长。