muduo源码分析:Acceptor类

Acceptor用于接受(accept)客户端的连接,通过设置回调函数通知使用者。它只在muduo网络库内部的TcpServer使用,由TcpServer控制它的生命期。

实际上,Acceptor只是对 Channel 的封装,通过Channel关注listenfd的 readable可读事件 ,并设置好回调函数就可以了。因此理解了上一节的muduo:Reactor,那么Acceptor也比较容易理解。


 

Acceptor.h

class Acceptor : boost::noncopyable
{
 public:
  typedef boost::function<void (int sockfd, const InetAddress&)> NewConnectionCallback;

  Acceptor(EventLoop* loop, const InetAddress& listenAddr, bool reuseport);
  ~Acceptor();

  void setNewConnectionCallback(const NewConnectionCallback& cb)	//设置新连接处理回调
  { newConnectionCallback_ = cb; }

  bool listenning() const { return listenning_; }
  void listen();		//启动监听套接字

 private:
  void handleRead();		//处理新连接到来的函数

  EventLoop* loop_;
  Socket acceptSocket_;		//监听套接字,Socket是个RAII型,析构时自动close文件描述符
  Channel acceptChannel_;	//通过channel,设置监听套接字的readable事件以及回调函数

  //NewConnectionCallback是:typedef NewConnectionCallback boost::function<void(int sockfd, const InetAddress&)>
  NewConnectionCallback newConnectionCallback_;
  bool listenning_;
  int idleFd_;
};

Acceptor::Acceptor() 构造

Acceptor::Acceptor(EventLoop* loop, const InetAddress& listenAddr, bool reuseport)
  : loop_(loop),
    acceptSocket_(sockets::createNonblockingOrDie(listenAddr.family())),
    acceptChannel_(loop, acceptSocket_.fd()),
    listenning_(false),
    idleFd_(::open("/dev/null", O_RDONLY | O_CLOEXEC))
{
  assert(idleFd_ >= 0);
  acceptSocket_.setReuseAddr(true);
  acceptSocket_.setReusePort(reuseport);
  acceptSocket_.bindAddress(listenAddr);
  acceptChannel_.setReadCallback(
      boost::bind(&Acceptor::handleRead, this));		//设置监听套接字可读回调。
}

Acceptor::~Acceptor() 析构

Acceptor::~Acceptor()
{
  acceptChannel_.disableAll(); // 移除注册的事件
  acceptChannel_.remove();     // Poller会持有Channel的裸指针,所以需要将该Channel从Poller中删除,避免Channel析构后,Poller持有空悬指针。
  ::close(idleFd_);
}

Acceptor::listen()

void Acceptor::listen()
{
  loop_->assertInLoopThread();
  listenning_ = true;
  acceptSocket_.listen();					//listen
  acceptChannel_.enableReading();				//注册监听套接字channel可读事件
}

注意此处注册可读事件,发生新连接时会调用Channel的 handleEvent 从而调用readCallback_ (即构造函数设置的handleRead())


Acceptor::handleRead()

在有新连接时被调用。

void Acceptor::handleRead()		//监听套接字可读事件的处理函数,有新连接时被调用
{
  loop_->assertInLoopThread();
  InetAddress peerAddr;

  int connfd = acceptSocket_.accept(&peerAddr);		//接受新连接
  if (connfd >= 0)				//当新连接成功建立
  {
    if (newConnectionCallback_)	
    {
      newConnectionCallback_(connfd, peerAddr);	//执行用户回调
    }
    else
    {
      sockets::close(connfd);
    }
  }
  else
  {
    LOG_SYSERR << "in Acceptor::handleRead";
 
    if (errno == EMFILE)
    {
      ::close(idleFd_);
      idleFd_ = ::accept(acceptSocket_.fd(), NULL, NULL);
      ::close(idleFd_);
      idleFd_ = ::open("/dev/null", O_RDONLY | O_CLOEXEC);
    }
  }
}

使用示例:

void newConnection(int sockfd, struct sockaddr &in_addr, socklen_t in_len)
{
    printf_address(sockfd, &in_addr, in_len);
    ::write(sockfd, "jinger\n", 8);
    ::close(sockfd);
}

int main()
{
    muduo::net::EventLoop loop;
    muduo::net::Acceptor acceptor(&loop, "8090");
    acceptor.setNewConnectionCallback(newConnection);
    acceptor.listen();
    loop.loop();
    return 0;
}

使用者只需要设置好回调函数然后listen即可。socket->bind->listen->accept 这些步骤在底层都已经封装好了。

猜你喜欢

转载自blog.csdn.net/amoscykl/article/details/83627023