源码下载以及安装点击链接https://blog.youkuaiyun.com/YoungSusie/article/details/90021742
Reactor 事件处理框架如图
不同于传统的Reactor,将timers 做成循环中单独的一步,muduo将 timers 和IO handlers 视为等同的。
1、Acceptor class
Acceptor class 用于 accept 新的TCP 连接, 并通过回调通知使用者,这是一个内部class , 供 Tcpserver 使用,Acceptor class 的生命期 由 Tcpserver 从 控制。
Acceptor class 的数据成员包括 Socket、Channel等。其中Socket 是一个RAII handle,封装 了socket 文件描述符的生命期。Acceptor 的socket 是listening socket,即server socket。acceptChannel 用于观察此socket 上的readable 事件,并且回调acceptor::handleRead(),handleRead()会调用accept 来接受新连接,并且回调用户的callback。
Acceptor::listen() 函数的最后一步让 acceptChannel在socket 可读 的时候回调Acceptor::handleRead()。Acceptor::handleRead()会接受accept(),并且回调newConnectionCallback。
Acceptor::handleRead()的策略很简单,每次accept 一个socket,另外还有两种实现:一是每次循环accept,直到没有新的连接到达;另外一个是每次尝试accept N 个新连接。后面两种方法比较适合短链接。
1、InetAddress class 对 struct sockaddr_in 的简单封装
2、Socket class 套接字的封装
3、SocketsOps.cc 主要是Socket 相关处理的函数
4、Acceptor class 用于接受新的 tcp 连接。
2、TCP接受新连接
(1)TcpConnection class
TcpConnection class 是最复杂的class,这部分只是先实现这个class的一部分,TcpConnection class 是 唯一默认使用shared_ptr 来管理的class,也是唯一继承 enable_shared_from_this 的class,TcpConnection class 对象是短命对象,不同于 TcpServer 是长命对象。对于短命对象,其生命期的控制不一定完全被控制,例如:对方客户端断开了某个TCP socket ,其对应的服务端进程中的TcpConnection 对象(是一个堆对象)的生命也即将走到尽头。但是此时我们不能立刻 delete 这个对象,因为其他地方可能还持有其引用。用智能指针控制TcpConnection class 的生命期更安全。
TcpConnection 没有可以供用户直接调用的函数,TcpConnection class 有两个状态:连接与未连接。TcpConnection class 使用channel 来获得socket 上的IO 事件,它自己处理writeable 事件,将 readable(接收数据) 事件通过messagecallbak 传给客户,TcpConnection class 拥有自己的Tcp socket, 析构函数中自动关闭这个socket。
TcpConnection class 表示的是一次tcp连接,是不可再生的,一旦连接断开,这个对象就没用了。TcpConnection class 也没有发起连接的功能,其构造函数中的参数是已经建好连接的sockfd 。
==================================TcpConnection.h ====================================
#ifndef _TCPCONNECTION_H
#define _TCPCONNETCION_H
#include "InetAddress.h"
#include <boost/function.hpp>
#include <boost/noncopyable.hpp>
#include <boost/any.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
class TcpConnection;
typedef boost::shared_ptr<TcpConnection> TcpConnectionPtr;
typedef boost::function<void (const TcpConnectionPtr&)> ConnectionCallback;
typedef boost::function<void (const TcpConnectionPtr&,const char* data,ssize_t len)> MessageCallback;
class Channel;
class EventLoop;
class Socket;
class TcpConnection : boost::noncopyable, public boost::enable_shared_from_this<TcpConnection>
{
public:
TcpConnection(EventLoop* loop, const std::string & name,int sockfd,
const InetAddress & localAddr, const InetAddress & peerAddr);
~TcpConnection();
EventLoop* getLoop() const {return loop_;}
const std::string name() const {return name_;}
const InetAddress & localAddress() {return localAddr_;}
const InetAddress & peerAddress() {return peerAddr_;}
bool connected() const {return state_ == kConnected;}
void setConnectionCallback(const ConnectionCallback & cb)
{ connectionCallback_ = cb;}
void setMessageCallback(const MessageCallback & cb)
{ messageCallback_ = cb;}
void connectEstablished();
private:
enum StateE { kConnecting,kConnected, };
void setState(StateE s) {state_ = s;}
void handleRead();
EventLoop * loop_;
std::string name_;
StateE state_;
boost::scoped_ptr<Socket> socket_;
boost::scoped_ptr<Channel> channel_;
InetAddress localAddr_;
InetAddress peerAddr_;
ConnectionCallback connectionCallback_;
MessageCallback messageCallback_;
};
#endif
==================================TcpConnection.cc ====================================
#include "TcpConnection.h"
#include "Channel.h"
#include "EventLoop.h"
#include "Socket.h"
#include <boost/bind.hpp>
#include <stdio.h>
#include <errno.h>
TcpConnection::TcpConnection(EventLoop * loop,const std::string & nameArg,int sockfd,
const InetAddress & localAddr, const InetAddress & peerAddr)
: loop_(loop),name_(nameArg),state_(kConnecting),
socket_(new Socket(sockfd)),channel_(new Channel(loop,sockfd)),
localAddr_(localAddr),peerAddr_(peerAddr)
{
std::cout << "TcpConnection :: stor[" << name_ << "] at " << this << " fd = " << sockfd << std::endl;
channel_->setReadCallback(boost::bind(&TcpConnection::handleRead,this));
}
TcpConnection::~TcpConnection()
{
std::cout << "TcpConnection::dtor[" << name_ << "] at " << this << " fd = " << channel_->fd() << std::endl;
}
void TcpConnection::connectEstablished()
{
loop_->assertInLoopThread();
assert(state_ == kConnecting);
setState(kConnected);
channel_->enableReading();
connectionCallback_(shared_from_this());
}
void TcpConnection::handleRead()
{
char buf[65536];
ssize_t n = ::read(channel_->fd(),buf, sizeof buf);
messageCallback_(shared_from_this(),buf,n);
}
目前TcpConnection class 只处理了建立连接,没有处理断开连接,后面再完善。
(2) TcpServer class
TcpServer class 的功能是管理accept 获得的 TcpConnection 。TcpServer class 是供用户直接使用的,生命期由用户控制,用户需要设置好callback,再调用start() 即可。
TcpServer class 的内部使用 acceptor 来获得新连接的fd。TcpServer 持有目前存活的TcpConnection 的 shared_ptr , 每个TcpConnection 对象有一个名字,这个名字是其所属的TcpServer 在创建 TcpConnection 对象时生成,名字是ConnectionMap 的 key,在新连接到来时,Acceptor 会回调newConnetion() ,后者会创建TcpConnection 对象,将它加入 ConnectionMap ,设置好callback,再调用TcpConnection-> connectEstablished() 。
==================================TcpServer.h ====================================
#ifndef _TCPSERVER_H
#define _TCPSERVER_H
#include "TcpConnection.h"
#include <map>
#include <boost/noncopyable.hpp>
#include <boost/scoped_ptr.hpp>
class Acceptor;
class EventLoop;
class TcpServer : boost::noncopyable
{
public:
TcpServer(EventLoop * loop, const InetAddress & listenAddr);
~TcpServer();
void start();
void setConnectionCallback(const ConnectionCallback & cb)
{ connectionCallback_ = cb;}
void setMessageCallback(const MessageCallback & cb)
{ messageCallback_ = cb;}
private:
void newConnection(int sockfd, const InetAddress & peerAddr); //不是线程安全的
typedef std::map<std::string,TcpConnectionPtr> ConnectionMap;
EventLoop * loop_;
const std::string name_;
boost::scoped_ptr<Acceptor> acceptor_;
ConnectionCallback connectionCallback_;
MessageCallback messageCallback_;
bool started_;
int nextConnId_;
ConnectionMap connections_;
};
#endif
==================================TcpServer.cc ====================================
#include "TcpServer.h"
#include "Acceptor.h"
#include "EventLoop.h"
#include "SocketsOps.h"
#include <boost/bind.hpp>
#include <stdio.h>
TcpServer::TcpServer(EventLoop * loop, const InetAddress & listenAddr)
:loop_(loop), name_(listenAddr.toHostPort()),
acceptor_(new Acceptor(loop, listenAddr)),
started_(false),nextConnId_(1)
{
std::cout << "tcpserver init\n";
acceptor_->setNewConnectionCallback(boost::bind(&TcpServer::newConnection,this,_1,_2));
}
TcpServer::~TcpServer()
{}
void TcpServer::start()
{
std::cout << "tcpserver start\n";
if(!started_)
{ started_ = true;}
if(!acceptor_->listening())
{
loop_->runInLoop(boost::bind(&Acceptor::listen,get_pointer(acceptor_)));
}
std::cout << "tcpserver start done\n";
}
void TcpServer::newConnection(int sockfd, const InetAddress & peerAddr)
{
loop_->assertInLoopThread();
char buf[32];
snprintf(buf,sizeof buf, "#%d" , nextConnId_);
++nextConnId_;
std::string connName = name_ + buf;
InetAddress localAddr(sockets::getLocalAddr(sockfd));
TcpConnectionPtr conn(
new TcpConnection(loop_, connName, sockfd, localAddr, peerAddr));
connections_[connName] = conn;
conn->setConnectionCallback(connectionCallback_);
conn->setMessageCallback(messageCallback_);
conn->connectEstablished();
}