Muduo Acceptor连接类

Acceptor连接

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_;			//主线程Loop
  Socket acceptSocket_;		//监听连接的socket
  Channel acceptChannel_;	//上述socket对应的Channel
  NewConnectionCallback newConnectionCallback_;	//accept()成功后执行的回调
  bool listening_;			//是否开启监听连接
  int idleFd_;				//用于处理busy-loop
};

构造函数

Acceptor::Acceptor(EventLoop* loop, const InetAddress& listenAddr, bool reuseport)
  : loop_(loop),
    acceptSocket_(sockets::createNonblockingOrDie(listenAddr.family())),//创建一个非阻塞socket套接字
    acceptChannel_(loop, acceptSocket_.fd()),		//创建监听连接channel
    listening_(false),
    idleFd_(::open("/dev/null", O_RDONLY | O_CLOEXEC))	// 处理busy-loop
{
  assert(idleFd_ >= 0);
  acceptSocket_.setReuseAddr(true);		//地址复用
  acceptSocket_.setReusePort(reuseport);//根据参数设置端口复用 与上面一条结合在一起取消Time_wait状态
  acceptSocket_.bindAddress(listenAddr); //绑定地址
  acceptChannel_.setReadCallback(  //设置可读回调,服务器收到连接后执行的函数
      std::bind(&Acceptor::handleRead, this));
}

先创建了一个socket,然后用这个socket初始化acceptChannel_,并且打开了"/dev/null"被idleFd保存,但是在构造函数中并没有开始监听用户连接,这个操作被封装成为一个函数,在TcpServer::star()中调用

void Acceptor::listen()
{
  loop_->assertInLoopThread();
  listening_ = true;
  acceptSocket_.listen();			//启动listen() 监听连接
  acceptChannel_.enableReading();	//向loop中加入读事件回调 可读时调用handleRead
}

接下来看读回调

void Acceptor::handleRead()
{
  loop_->assertInLoopThread();	//是否在IO线程中
  InetAddress peerAddr;	//struct sockaddr_in addr
  //FIXME loop until no more
  int connfd = acceptSocket_.accept(&peerAddr);	//peerAddr用于获取对端的协议地址
  if (connfd >= 0)
  {
    // string hostport = peerAddr.toIpPort();
    // LOG_TRACE << "Accepts of " << hostport;
    if (newConnectionCallback_)	//连接回调,在TcpServer的构造函数中设置为 TcpServer::newConnection
    {
      newConnectionCallback_(connfd, peerAddr);	//把刚刚accept的cfd和addr传入
    }
    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);
    }
  }
}

成功获取到客端连接后生成了fd再调用回调newConnectionCallback_(),该回调在TcpServer的构造函数中被设置.

这里说下不成功的操作,也就是busy-loop,这种情况是指该进程中的文件描述被耗尽了,故而返回错误号 errno == EMFILE

那么没们在构造函数中打开的一个文件描述符就可以派上用场了,我们关闭该描述然后再次accpet(),此时就可以成功接收到

客端的连接,然后再把客端的连接关闭,再次打开"/dev/null",就可以通知客端刚刚你发起的连接失败了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值