muduo源码剖析之Acceptor监听类

简介

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

cpp 复制代码
#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

cpp 复制代码
// 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);
    }
  }
}
相关推荐
计算机学姐18 分钟前
基于Python的高校成绩分析管理系统
开发语言·vue.js·后端·python·mysql·pycharm·django
迈威通信20 分钟前
迈威通信助力海上探测,守护蓝色疆域
网络·自动化·能源
脱了格子衬衫37 分钟前
linux安装ansible
linux·运维·ansible
励志成为嵌入式工程师1 小时前
c语言选择排序
c语言·算法·排序算法
小丑西瓜6661 小时前
MySQL库操作
linux·服务器·数据库·mysql
三小尛1 小时前
希尔排序(C语言)
c语言·数据结构·排序算法
風清掦1 小时前
C/C++每日一练:编写一个查找子串的位置函数
c语言·c++·算法
wclass-zhengge1 小时前
SpringCloud篇(服务拆分 / 远程调用 - 入门案例)
后端·spring·spring cloud
三小尛1 小时前
插入排序(C语言)
c语言·开发语言
荒川之神1 小时前
RHEL/CENTOS 7 ORACLE 19C-RAC安装(纯命令版)
服务器·数据库·oracle