- Acceptor用于接收client的连接请求,建立连接
- Acceptor的主要功能包括socket、bind、listen,并调用注册的回调函数来处理新到的连接。
- Acceptor主要负责的就是对lfd的读事件的处理,当有读事件到来的时候,即会调用handleRead()这个函数来调用构造时传给他的回调函数。
- Acceptor的数据成员包括Socket、Channel,Acceptor的socket是listening
socket(即serversocket)。Channel用于观察此socket的readable事件,并回调Accptor::handleRead(),后者调用accept(2)来接受新连接,并回调用户callback。 - Acceptor类在上层应用程序中我们不直接使用,而是把它封装作为TcpServer的成员,生命期由后者控制。
数据成员:
EventLoop* loop_:loop_指向所属的EventLoop
Socket acceptSocket_:监听套接字acceptSocket_
Channel acceptChannel_:通道acceptChannel_,关注套接字acceptSocket_的可读事件
NewConnectionCallback newConnectionCallback_:连接建立时调用的回调函数newConnectionCallback_
bool listenning_:是否处于监听的状态
int idleFd_:预先准备一个空闲的文件描述符
typedef
typedef boost::function<void (int sockfd, const InetAddress&)> NewConnectionCallback;
成员函数:
Acceptor(EventLoop* loop, const InetAddress& listenAddr):构造函数,需Acceptor通过一个EventLoop指针,一个InetAddress对象,以及一个是否复用端口的标志构造,构造函数中会创建监听套接字并绑定响应的Channel,但是并不会启动关注Channel的可读事件,这是listen()中应该做的。并且还创建了一个指向空的文件描述符,
~EventLoopThread():析构函数
void setNewConnectionCallback(const NewConnectionCallback& cb):注册连接建立时调用的回调函数newConnectionCallback_
bool listenning() const:返回是否处于监听状态
void listen():监听函数
void handleRead():套接字可读时调用的回调函数,用来注册acceptChannel_的ReadCallback()函数
Acceptor.h
// This is an internal header file, you should not include this.
#ifndef MUDUO_NET_ACCEPTOR_H
#define MUDUO_NET_ACCEPTOR_H
#include <boost/function.hpp>
#include <boost/noncopyable.hpp>
#include <muduo/net/Channel.h>
#include <muduo/net/Socket.h>
namespace muduo
{
namespace net
{
class EventLoop;
class InetAddress;
///
/// Acceptor of incoming TCP connections.
///
class Acceptor : boost::noncopyable
{
public:
typedef boost::function<void (int sockfd,
const InetAddress&)> NewConnectionCallback;
Acceptor(EventLoop* loop, const InetAddress& listenAddr);
~Acceptor();
//注册连接建立时调用的回调函数newConnectionCallback_
void setNewConnectionCallback(const NewConnectionCallback& cb)
{ newConnectionCallback_ = cb; }
//返回是否处于监听状态
bool listenning() const { return listenning_; }
//监听函数
void listen();
private:
// 套接字可读时调用的回调函数,用来注册acceptChannel_的ReadCallback()函数
void handleRead();
//loop_指向所属的EventLoop
EventLoop* loop_;
//监听套接字acceptSocket_
Socket acceptSocket_;
//通道acceptChannel_,关注套接字acceptSocket_的可读事件
Channel acceptChannel_;
//连接建立时调用的回调函数newConnectionCallback_
NewConnectionCallback newConnectionCallback_;
//是否处于监听的状态
bool listenning_;
//预先准备一个空闲的文件描述符
int idleFd_;
};
}
}
#endif // MUDUO_NET_ACCEPTOR_H
Acceptor.cc
#include <muduo/net/Acceptor.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/InetAddress.h>
#include <muduo/net/SocketsOps.h>
#include <boost/bind.hpp>
#include <errno.h>
#include <fcntl.h>
//#include <sys/types.h>
//#include <sys/stat.h>
using namespace muduo;
using namespace muduo::net;
Acceptor::Acceptor(EventLoop* loop, const InetAddress& listenAddr)
: loop_(loop),
//创建套接字acceptSocket_
acceptSocket_(sockets::createNonblockingOrDie()),
//acceptChannel_关注套接字acceptSocket_的一些事件
acceptChannel_(loop, acceptSocket_.fd()),
listenning_(false),
//预先准备一个空闲的文件描述符
idleFd_(::open("/dev/null", O_RDONLY | O_CLOEXEC))
{
assert(idleFd_ >= 0);
acceptSocket_.setReuseAddr(true);
acceptSocket_.bindAddress(listenAddr);
//设置通道acceptChannel_可读回调函数为handleRead()
acceptChannel_.setReadCallback(
boost::bind(&Acceptor::handleRead, this));
}
Acceptor::~Acceptor()
{
//把通道acceptChannel_上所有事件都disableAll()掉
acceptChannel_.disableAll();
acceptChannel_.remove();
::close(idleFd_);
}
void Acceptor::listen()
{
loop_->assertInLoopThread();
listenning_ = true;
//调用套接字的listen()函数
acceptSocket_.listen();
//acceptChannel_关注套接字的可读事件
acceptChannel_.enableReading();
}
void Acceptor::handleRead()
{
loop_->assertInLoopThread();
//准备一个存储对等方地址的peerAddr
InetAddress peerAddr(0);
//FIXME loop until no more
//将对等方地址存入peerAddr
int connfd = acceptSocket_.accept(&peerAddr);
if (connfd >= 0)
{
// string hostport = peerAddr.toIpPort();
// LOG_TRACE << "Accepts of " << hostport;
if (newConnectionCallback_)
{
newConnectionCallback_(connfd, peerAddr);
}
else
{
sockets::close(connfd);
}
}
else
{
// Read the section named "The special problem of
// accept()ing when you can't" in libev's doc.
// By Marc Lehmann, author of livev.
if (errno == EMFILE)
{
::close(idleFd_);
idleFd_ = ::accept(acceptSocket_.fd(), NULL, NULL);
::close(idleFd_);
idleFd_ = ::open("/dev/null", O_RDONLY | O_CLOEXEC);
}
}
}