该类对象从属于TcpServer类对象。主要作用就是listen(),accept(),然后accpet()成功后调用NewConnectionCallback回调函数.
1.Acceptor.h文件
///
/// Acceptor of incoming TCP connections.
///
class Acceptor : noncopyable
{
public:
///连接建立成功后将调用的回调函数
typedef std::function<void (int sockfd, const InetAddress&)> NewConnectionCallback;
///loop:从属的loop, listenAddr:绑定对象(bind()该对象) reuseport:决定是否开启SO_REUSEPORT选项
Acceptor(EventLoop* loop, const InetAddress& listenAddr, bool reuseport);
~Acceptor();
//设置成功建立连接后的回调函数
void setNewConnectionCallback(const NewConnectionCallback& cb)
{ newConnectionCallback_ = cb; }
///开启监听状态,内部主要两个动作:1.内部的Soket类型对象(acceptSocket_)调用listen启动监听
///2.更改内部的Channel类型(acceptChannel_)监听事件的状态,
///从而更改poller(就是epoll_wait()或者poll())对其channel的操作
void listen();
///返回是否监听的状态
bool listening() const { return listening_; }
private:
///实际内部调用accept()的函数,然后建立连接成功后会调用newConnectionCallback_函数
void handleRead();
EventLoop* loop_; ///从属的loop
Socket acceptSocket_; ///构造函数生成的Socket对象
Channel acceptChannel_; ///构造函数生成的Channel对象
NewConnectionCallback newConnectionCallback_; ///建立连接成功后调用的函数
bool listening_; ///是否处于监听状态
int idleFd_; ///空设备文件/dev/null 指定的文件描述符
};
2.Acceptor.cc文件
Acceptor::Acceptor(EventLoop* loop, const InetAddress& listenAddr, bool reuseport)
: loop_(loop), //获得从属的loop
acceptSocket_(sockets::createNonblockingOrDie(listenAddr.family())),///构造一个Socket对象
acceptChannel_(loop, acceptSocket_.fd()),///构造一个Channel对象
listening_(false), ///初始时未开启监听状态
idleFd_(::open("/dev/null", O_RDONLY | O_CLOEXEC)) ///打开空设备文件
{
assert(idleFd_ >= 0);
acceptSocket_.setReuseAddr(true); ///开启SO_REUSEADDR选项
acceptSocket_.setReusePort(reuseport);///设置SO_REUSEPORT是否开启
acceptSocket_.bindAddress(listenAddr);///绑定ip和port
acceptChannel_.setReadCallback( ///设置读回调事件
std::bind(&Acceptor::handleRead, this));
}
Acceptor::~Acceptor()
{
acceptChannel_.disableAll(); ///使其不监听任何事件
acceptChannel_.remove(); ///poller不监听channel对象了,但要保证loop对象中的活动channel列表不包含该对象了
::close(idleFd_); //关闭 空设备文件
}
void Acceptor::listen()
{
loop_->assertInLoopThread(); ///断言(loop进程)
listening_ = true; //将此对象置为监听状态
acceptSocket_.listen(); //调用listen()函数
acceptChannel_.enableReading(); //使其监听读事件
}
void Acceptor::handleRead()
{
loop_->assertInLoopThread();
InetAddress peerAddr; //对端地址
//FIXME loop until no more
int connfd = acceptSocket_.accept(&peerAddr); //内部调用的就是普通的accept()函数
if (connfd >= 0)
{
// string hostport = peerAddr.toIpPort();
// LOG_TRACE << "Accepts of " << hostport;
if (newConnectionCallback_) //建立连接成功就调用回调函数
{
newConnectionCallback_(connfd, peerAddr);
}
else
{
sockets::close(connfd); //没有设置回调函数就关闭此连接
}
}
else
{
LOG_SYSERR << "in Acceptor::handleRead";
// Read the section named "The special problem of
// accept()ing when you can't" in libev's doc.
// By Marc Lehmann, author of libev.
if (errno == EMFILE) ///打开了太多的文件描述符
{
::close(idleFd_); //关闭这个指向空设备文件的文件描述符,这样就有一个可用的文件描述符了
idleFd_ = ::accept(acceptSocket_.fd(), NULL, NULL); //由于是水平触发模式,所以会一直导致触发。
::close(idleFd_); //释放掉该连接对象
idleFd_ = ::open("/dev/null", O_RDONLY | O_CLOEXEC); ///然后重新指向空设备文件
}
}
}