简介
Acceptor类用于创建套接字,设置套接字选项,调用socket()->bind()->listen()->accept()函数,接受连接,然后调用TcpServer设置的connect事件的回调。
listen()//在TcpServer::start中调用
封装了一个listen fd相关的操作,用于mainLoop
成员及属性解析
Acceptor - 逻辑上的内部类
接受器封装,实质上就是对Channel的多一层封装
主要接口
listen
监听连接
当新连接进入时,调用Socket::accept创建套接字,触发TcpServer的回调
setNewConnectionCallback
TcpServer通过该接口设置回调,当新连接套接字创建后,创建TcpConnection对象
核心实现:
通过socket::accept接受新连接,获得套接字fd
这个fd作为参数调用TcpServer注册的回调
主要成员
- loop
- channel
- idlefd
非常巧妙的设计,在服务器压力过大,无法新建文件描述符时,通过这个idlefd拒绝连接
来自libevent的设计
源码剖析
Acceptor.h
#ifndef MUDUO_NET_ACCEPTOR_H
#define MUDUO_NET_ACCEPTOR_H
#include <functional>
#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 : noncopyable
{
public:
typedef std::function<void (int sockfd, const InetAddress&)> NewConnectionCallback;
Acceptor(EventLoop* loop, const InetAddress& listenAddr, bool reuseport);
~Acceptor();
void setNewConnectionCallback(const NewConnectionCallback& cb)
{
newConnectionCallback_ = cb; }
void listen();
bool listening() const {
return listening_; }
// Deprecated, use the correct spelling one above.
// Leave the wrong spelling here in case one needs to grep it for error messages.
// bool listenning() const { return listening(); }
private:
void handleRead();
EventLoop* loop_;
Socket acceptSocket_;
Channel acceptChannel_;
NewConnectionCallback newConnectionCallback_;
bool listening_;
int idleFd_;
};
} // namespace net
} // namespace muduo
#endif // MUDUO_NET_ACCEPTOR_H
Acceptor.cc
// Copyright 2010, Shuo Chen. All rights reserved.
// http://code.google.com/p/muduo/
//
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.
// Author: Shuo Chen (chenshuo at chenshuo dot com)
#include "muduo/net/Acceptor.h"
#include "muduo/base/Logging.h"
#include "muduo/net/EventLoop.h"
#include "muduo/net/InetAddress.h"
#include "muduo/net/SocketsOps.h"
#include <errno.h>
#include <fcntl.h>
//#include <sys/types.h>
//#include <sys/stat.h>
#include <unistd.h>
using namespace muduo;
using namespace muduo::net;
Acceptor::Acceptor(EventLoop* loop, const InetAddress& listenAddr, bool reuseport)
: loop_(loop),
acceptSocket_(sockets::createNonblockingOrDie(listenAddr.family())),//创建一个非阻塞的socket fd
acceptChannel_(loop, acceptSocket_.fd()),//创建socket fd的channel
listening_(false),//是否处于监听状态
idleFd_(::open("/dev/null", O_RDONLY | O_CLOEXEC))
{
assert(idleFd_ >= 0);
acceptSocket_.setReuseAddr(true); //设置ip地址复用
acceptSocket_.setReusePort(reuseport); //设置端口复用
acceptSocket_.bindAddress(listenAddr); //bind()函数封装,绑定ip和端口
acceptChannel_.setReadCallback(
std::bind(&Acceptor::handleRead, this));//设置accept的回调函数
}
Acceptor::~Acceptor()
{
acceptChannel_.disableAll();//将mainloop poller监听集合中移除,取消所有事件的监听
acceptChannel_.remove();//在events_删除channel
::close(idleFd_);//关闭文件
}
void Acceptor::listen()//开始监听fd
{
loop_->assertInLoopThread();//判断是不是和创建时的io线程处于同一个线程
listening_ = true;//是否监听
acceptSocket_.listen();//真正的监听函数
acceptChannel_.enableReading();//设置监听读事件
}
void Acceptor::handleRead()//当有client connnect时,则会调用
{
loop_->assertInLoopThread();
InetAddress peerAddr;
//FIXME loop until no more
int connfd = acceptSocket_.accept(&peerAddr);//接收client connect,返回值==accept()返回值
if (connfd >= 0)
{
// string hostport = peerAddr.toIpPort();
// LOG_TRACE << "Accepts of " << hostport;
if (newConnectionCallback_)//如果设置了connect cb,则调用,否则则关闭这个连接
{
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);
}
}
}