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);
    }
  }
}
相关推荐
wen__xvn15 分钟前
计算机网络1-5:计算机网络的性能指标
网络·计算机网络
苇柠18 分钟前
Spring框架基础(1)
java·后端·spring
学习网安的doro23 分钟前
3a服务器的基本功能1之身份认证
服务器·网络·学习·安全·身份认证·ac
Lovyk24 分钟前
DNS 服务器
运维·服务器
望获linux25 分钟前
【实时Linux实战系列】实时数据流处理框架分析
linux·运维·前端·数据库·chrome·操作系统·wpf
xiucai_cs31 分钟前
布隆过滤器原理与Spring Boot实战
java·spring boot·后端·布隆过滤器
搬码临时工33 分钟前
如何设置端口映射?防火墙/路由器/纯软件工具多种常用方案步骤,确保任意内网ip端口映射公网访问到
网络·网络协议·tcp/ip
向阳花自开33 分钟前
Spring Boot 常用注解速查表
java·spring boot·后端
程序视点1 小时前
如何高效率使用 Cursor ?
前端·后端·cursor
zxyzxyzxyzxyzz1 小时前
Ubuntu设置Samba文件共享
linux·服务器·ubuntu