之前想学muduo 但因为一些原因,暂时编译不过,碰巧看到一个博主是模仿着muduo写的mini-muduo,看了看觉得挺好,仅仅实现Echo服务器,从最简单的epoll模型开始到反应堆+多线程,为了简洁没有使用智能指针(它也提示了会内存泄漏),部分有bug,但依旧是一个很好的学习对象。本文就最后的 反应堆+多线程模型作一下分析。(原文分成了13小节)
main.cpp
#include "TcpServer.h"
#include "EventLoop.h"
#include "EchoServer.h"
int main(int args, char** argv)
{
EventLoop loop;
EchoServer echoserver(&loop);
echoserver.start();
loop.loop();
return 0;
}
接口都是一样的,一个服务器,一个loop,分别使用start loop方法就可以让程序跑起来,
EchoServer.h
#ifndef ECHOSERVER_H
#define ECHOSERVER_H
#include "IMuduoUser.h"
#include "IRun.h"
#include "TcpServer.h"
#include "ThreadPool.h"
class EchoServer : public IMuduoUser
, public IRun2
{
public:
EchoServer(EventLoop* pLoop);
~EchoServer();
void start();
virtual void onConnection(TcpConnection* pCon);
virtual void onMessage(TcpConnection* pCon, Buffer* pBuf);
virtual void onWriteComplate(TcpConnection* pCon);
virtual void run2(const string& str, void* tcp);
private:
int fib(int n);
EventLoop* _pLoop;
TcpServer _pServer;
ThreadPool _threadpool;
long _timer;
int _index;
};
#endif
这个EchoServer 是应用层面的服务器,继承于IMuduoUser和IRun2。IMuduoUser类包含onConnection,onMessage,onWriteComplate三个虚函数,要求子类EchoServer实现,分别对应着连接时,收到消息时,发送消息完成时需要的部分处理,
IRun2类虚函数是run2,这个函数运用于回调。
EventLoop* _pLoop是事件轮讯对象指针,TcpServer _pServer是服务器对象,ThreadPool _threadpool是线程池,_timer是计时器的地址(原博主的意思是用计时器的id,但他直接使用了计时器的指针存放在long类型的_timer里,可以说是有点古怪,后面需要多次的转型)
_index暂无使用
EchoServer.cc
#include "EchoServer.h"
#include "TcpConnection.h"
#include "EventLoop.h"
#include "CurrentThread.h"
#include "Task.h"
#include <iostream>
#define MESSAGE_LENGTH 8
EchoServer::EchoServer(EventLoop* pLoop)
:_pLoop(pLoop)
,_pServer(pLoop)
,_timer(-1)
,_index(0)
{
_pServer.setCallback(this);
}
EchoServer::~EchoServer()
{
}
void EchoServer::start()
{
_pServer.start();
_threadpool.start(3);
}
void EchoServer::onConnection(TcpConnection* pCon)
{
printf("%s\n",__func__);
cout << "onConnection" << endl;
}
void EchoServer::onMessage(TcpConnection* pCon, Buffer* pBuf)
{
printf("%s\n",__func__);
while(pBuf->readableBytes() > MESSAGE_LENGTH)
{
string message = pBuf->retrieveAsString(MESSAGE_LENGTH);
Task task(this, message, pCon);
_threadpool.addTask(task);
}
if (pBuf->readableBytes() <= MESSAGE_LENGTH){
string message = pBuf->retrieveAllAsString();
Task task(this, message, pCon);
_threadpool.addTask(task);
}
}
void EchoServer::onWriteComplate(TcpConnection* pCon)
{
printf("%s\n",__func__);
cout << "onWriteComplate" << endl;
}
//run in different therad
void EchoServer::run2(const string& str, void* tcp)
{
printf("%s\n",__func__);
//IO blocking task or CPU busy task
cout << "fib(30) = " << fib(30) << " tid = " << CurrentThread::tid() << endl;
((TcpConnection*)tcp)->send(str);
}
//fib is short for Fibonacci, fib is a CPU busy method
int EchoServer::fib(int n)
{
return (n == 1 || n == 2) ? 1 : (fib(n-1) + fib(n-2));
}
可以看到 onConnection,onWriteComplate类仅仅是打印一下函数名称, printf("%s\n",__func__);
是我为了打印函数执行流程而打印的,使用时可去除。
先不看run2 ,fib,onMessage,而是看main里面的echoserver.start
void EchoServer::start()
{
_pServer.start();
_threadpool.start(3);
}
转到TcpServer
TcpServer.h
//author voidccc
#ifndef TCPSERVER_H
#define TCPSERVER_H
#include <sys/epoll.h>
#include "Declear.h"
#include "Define.h"
#include "IAcceptorCallback.h"
#include "IMuduoUser.h"
#include <map>
using namespace std;
class TcpServer : public IAcceptorCallback
{
public:
TcpServer(EventLoop* pLoop);
~TcpServer();
void start();
void setCallback(IMuduoUser* pUser);
virtual void newConnection(int sockfd);
private:
struct epoll_event _events[MAX_EVENTS];
map<int, TcpConnection*> _connections;
Acceptor* _pAcceptor;
EventLoop* _pLoop;
IMuduoUser* _pUser;
};
#endif
TcpServer.cc
//author voidccc
#include <errno.h>
#include "TcpServer.h"
#include "Channel.h"
#include "Acceptor.h"
#include "TcpConnection.h"
#include <vector>
TcpServer::TcpServer(EventLoop* pLoop)
:_pAcceptor(NULL)
,_pLoop(pLoop)
,_pUser(NULL)
{
}
TcpServer::~TcpServer()
{
}
void TcpServer::start()
{
_pAcceptor = new Acceptor(_pLoop); // Memory Leak !!!
_pAcceptor->setCallback(this);
_pAcceptor->start();
}
void TcpServer::newConnection(int sockfd)
{
TcpConnection* tcp = new TcpConnection(sockfd, _pLoop); // Memory Leak !!!
_connections[sockfd] = tcp;
tcp->setUser(_pUser);
tcp->connectEstablished();
}
void TcpServer::setCallback(IMuduoUser* user)
{
_pUser = user;
}
直接看TcpServer.start里面new了一个Accpetor
Accpetor.h
//author voidccc
#ifndef ACCEPTOR_H
#define ACCEPTOR_H
#include "Declear.h"
#include "Define.h"
#include "IChannelCallback.h"
class Acceptor : public IChannelCallback
{
public:
Acceptor(EventLoop* pLoop);
~Acceptor();
void start();
void setCallback(IAcceptorCallback* pCallback);
virtual void handleRead();
virtual void handleWrite();
private:
int createAndListen();
int _listenfd;
Channel* _pSocketAChannel;
IAcceptorCallback* _pCallback;
EventLoop* _pLoop;
};
#endif
跳到Acceptor.cc内
直接看start,
Acceptor.cc
#include <arpa/inet.h>
#include <fcntl.h>
#include <errno.h>
#include "Acceptor.h"
#include "Channel.h"
#include "IAcceptorCallback.h"
#include "EventLoop.h"
#include <iostream>
using namespace std;
Acceptor::Acceptor(EventLoop* pLoop)
:_listenfd(-1)
,_pSocketAChannel(