Acceptor类一般由TCPServer创建,负责处理客户端发送的connect,它拥有一个acceptSocket_和acceptChannel_成员。

1、创建Acceptor :

TcpServer::TcpServer(EventLoop* loop,
const InetAddress& listenAddr,
const string& _name)
: loop_(CHECK_NOTNULL(loop)),
hostport_(listenAddr.toHostPort()),
name_(_name),
acceptor_(new Acceptor(loop, listenAddr)),
threadPool_(new EventLoopThreadPool(loop)),
connectionCallback_(defaultConnectionCallback),
messageCallback_(defaultMessageCallback),
started_(false),
nextConnId_(1)
{
acceptor_->setNewConnectionCallback(
boost::bind(&TcpServer::newConnection, this, _1, _2));
}

Acceptor创建非阻塞的listen socket和channel,并且将Acceptor::handleRead函数设置为acceptChannel_的Read Callback:

Acceptor::Acceptor(EventLoop* loop, const InetAddress& listenAddr, bool reuseport)
: loop_(loop),
acceptSocket_(sockets::createNonblockingOrDie()),      //create a listening socket
acceptChannel_(loop, acceptSocket_.fd()),                    //create listening channel
listenning_(false),
idleFd_(::open("/dev/null", O_RDONLY | O_CLOEXEC))

{
    acceptSocket_.setReuseAddr(true);
    acceptSocket_.bindAddress(listenAddr);
    acceptChannel_.setReadCallback(
    boost::bind(&Acceptor::handleRead, this));
 }

客户端每connect一次,服务端都会调用Acceptor::handleRead()进行处理:

void Acceptor::handleRead()
{loop_->assertInLoopThread();InetAddress peerAddr(0);//FIXME loop until no moreint connfd = acceptSocket_.accept(&peerAddr);if (connfd >= 0){// string hostport = peerAddr.toHostPort();// LOG_TRACE << "Accepts of " << hostport;if (newConnectionCallback_){newConnectionCallback_(connfd, peerAddr);}else{sockets::close(connfd);}}
}

再看看acceptSocket_.accept(&peerAddr)函数:

int sockets::accept(int sockfd, struct sockaddr_in* addr)
{socklen_t addrlen = sizeof *addr;
#if VALGRINDint connfd = ::accept(sockfd, sockaddr_cast(addr), &addrlen);setNonBlockAndCloseOnExec(connfd);
#elseint connfd = ::accept4(sockfd, sockaddr_cast(addr),&addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC);
#endifif (connfd < 0){int savedErrno = errno;LOG_SYSERR << "Socket::accept";switch (savedErrno){case EAGAIN:case ECONNABORTED:case EINTR:case EPROTO: // ???case EPERM:// expected errorsbreak;case EBADF:case EFAULT:case EINVAL:case EMFILE: // per-process lmit of open file desctiptor ???case ENFILE:case ENOBUFS:case ENOMEM:case ENOTSOCK:case EOPNOTSUPP:// unexpected errorsLOG_FATAL << "unexpected error of ::accept";break;default:LOG_FATAL << "unknown error of ::accept " << savedErrno;break;}}return connfd;
}

接受客户端的连接,同时设置连接socket为非阻塞方式。

由于listen socket是非阻塞方式,

2、下面看看Acceptor::acceptChannel_是怎么注册到Eventloop中去的:

void TcpServer::start()
{if (!started_){started_ = true;threadPool_->start();}if (!acceptor_->listenning()){loop_->runInLoop(boost::bind(&Acceptor::listen, get_pointer(acceptor_)));}
}

上面启动TcpServer时调用了Acceptor::listen(),这个函数顾名思义,就是开始监听对端的连接请求:

void Acceptor::listen()
{loop_->assertInLoopThread();listenning_ = true;acceptSocket_.listen();acceptChannel_.enableReading();
}

Acceptor::enableReading是个内联函数,设置acceptChannel_感兴趣的事件是kReadEvent = POLLIN | POLLPRI

void enableReading() { events_ |= kReadEvent; update(); }

我们注意到,上面的函数同时调用了Channel::update(),这个函数没有其他操作,直接调用了EventLoop::updateChannel(Channel* channel):

void Channel::update()
{loop_->updateChannel(this);
}

EventLoop::updateChannel(Channel* channel)函数如下:

void EventLoop::updateChannel(Channel* channel)
{assert(channel->getLoop() == this);assertInLoopThread();poller_->updateChannel(channel);
}

首先判断channel的loop是否是当前运行的loop以及updateChannel函数是否在当前loop线程中线程中调用的,可以看出:

1、一个channel只能属于一个loop,不能在其他loop中使用当前loop的channel;

2、一个loop只能在属于一个线程,one loop per thread。

接下来调用了poller_->updateChannel,muduo支持两种方式的IO复用:poll和epoll,默认为epoll方式,看下epoll的updateChannel干了什么:

void EPollPoller::updateChannel(Channel* channel)
{Poller::assertInLoopThread();LOG_TRACE << "fd = " << channel->fd() << " events = " << channel->events();if (channel->index() < 0){// a new one, add with EPOLL_CTL_ADDint fd = channel->fd();assert(channels_.find(fd) == channels_.end());channel->set_index(1);channels_[fd] = channel;update(EPOLL_CTL_ADD, channel);}else{// update existing one with EPOLL_CTL_MODint fd = channel->fd();assert(channels_.find(fd) != channels_.end());assert(channels_[fd] == channel);assert(channel->index() == 1);update(EPOLL_CTL_MOD, channel);}
}

Poller::assertInLoopThread()保证updateChannel是在其所属的loop线程中调用的;
channel::index_用来标识channel是否注册到给了EPoll。如果小于0,说明还没有注册到EPoll中,添加channel到map中,同时调用update(EPOLL_CTL_ADD, channel):如果大于0,调用update(EPOLL_CTL_MOD, channel)。

下面看看 EPollPoller::update:
void EPollPoller::update(int operation, Channel* channel)
{struct epoll_event event;bzero(&event, sizeof event);event.events = channel->events();event.data.ptr = channel;if (::epoll_ctl(epollfd_, operation, channel->fd(), &event) < 0){LOG_SYSFATAL << "epoll_ctl op=" << operation;}
}

这个地方很明白了,注册监听事件给内核,同时将当前通道的指针传给内核(注意没有把socket的fd传给内核),这个地方做的很巧妙,我们在检测到事件时就会明白这个地方的巧妙之处。

这样,一个Server端基本建立起来了,其中的关键就是怎么把listen socket和Epoll联系起来。

转载于:https://www.cnblogs.com/RobbieQiu/p/3573005.html

muduo学习笔记-Acceptor类相关推荐

  1. muduo学习笔记 日志类

    learn_muduo Logger Logger有六个日志等级 TRACE DEBUG INFO WARN ERROR FATAL 日志的输出语句是通过宏定义完成,编译期完成宏定义替换,创建Logg ...

  2. muduo学习笔记 线程类

    learn_muduo 线程属性 线程标识 pthreadId_,pid_t 线程函数 func_ 线程名字 name_ 线程序号 numCreated_ bool started_; // 线程状态 ...

  3. muduo学习笔记:net部分之Http--HttpServer

    前面[muduo学习笔记:net部分之Http–HttpRequest.HttpResponse 和 HttpContext]介绍了TCP数据数据Buffer承载的HTTP报文的解析,本文结合TcpS ...

  4. ASM学习笔记2 - 类的创建和修改 —— ClassWriter的综合应用

    ASM学习笔记2 - 类的创建和修改 -- ClassWriter的综合应用 上回我们说到,通过使用ClassVisitor和ClassReader,我们能够分析已经存在的类.这一节中,我们将使用Cl ...

  5. Python学习笔记 (类与对象)

    Python学习笔记 (类与对象) 1.类与对象 面向对象编程语言类: 一个模板, (人类)-是一个抽象的, 没有实体的对象: (eg: 张三, 李四) 属性: (表示这类东西的特征, 眼睛, 嘴巴, ...

  6. Machine Learning A-Z学习笔记12-分类模型性能评级及选择

    Machine Learning A-Z学习笔记12-分类模型性能评级及选择 1.简单原理 一般认为假阴性比假阳性更严重,如核酸检测 用混淆矩阵表示如下图 准确率驳论(Accuracy Paradox ...

  7. JAVA学习笔记(类的学习)

    JAVA学习笔记(类的学习) 类声明和类体 构造方法和对象创建 对象的引用和实体 成员变量 方法 方法重载 关键字this 包 import语句 访问权限 对象数组 反编译和文档生成器 JAR文件 1 ...

  8. python面向对象编程72讲_2020-07-22 Python学习笔记27类和面向对象编程

    一些关于自己学习Python的经历的内容,遇到的问题和思考等,方便以后查询和复习. 声明:本人学习是在扇贝编程通过网络学习的,相关的知识.案例来源于扇贝编程.如果使用请说明来源. 第27关 类与面向对 ...

  9. 设计模式学习笔记1——类与类之间的关系

    1.继承关系 概念 继承又分为2种方式,一种叫实现继承,一种叫接口继承.参见笔记--[2种继承方式学习笔记].(https://blog.csdn.net/yhb1206/article/detail ...

最新文章

  1. 8.1shell介绍 8.2命令历史 8.3命令补全和别名 8.4通配符 8.5输入输出重定向
  2. 中国研制600公里时速磁悬浮:北京到上海,仅需3.5小时左右
  3. 考前自学系列·计算机组成原理·微程序微指令微命令微操作
  4. linux+向进城发送信号,信号 - it610.com
  5. UVA - 572 Oil Deposits-dfs找连通块
  6. Python 第三方模块之 psutil - 获取系统运行的进程和系统利用率信息
  7. android 表情,软键盘冲突解决方案(仿微博等SNS应用)
  8. EFCore-一对一配置外键小记
  9. 【C语言】qsort函数用法(转)
  10. 数据挖掘-朴素贝叶斯分类
  11. ApacheCN Java 译文集 20210921 更新
  12. PRIMARY KEY 与 UNIQUE
  13. Contrastive Loss (对比损失)
  14. python unit test什么意思_python中的unittest有什么作用
  15. 供应链金融与区块链02——论文阅读
  16. 浅谈如何保障服务器安全
  17. 图片去黑底原理(做个笔记)
  18. linux中c语言kbhit函数用法,检测按键(Linux中kbhit()函数的实现)
  19. 用Scrapy爬取笔趣阁小说
  20. python中random模块

热门文章

  1. scss在Vue中的安装及使用
  2. android读出序列号,获取android手机序列号
  3. 显微镜下的webpack4入门
  4. 鸿蒙DevEco本地模拟器提示不支持的CPU解决办法
  5. 缩小贫富差距和增加人口的唯一办法
  6. 学着解决问题(提问的智慧)
  7. 濉溪一中2021年高考成绩查询,淮北高考成绩查询系统2021
  8. javascript链表
  9. 一个测试工程师的七年感悟——致在一路独行的你(别放弃)
  10. 密码学的发展(第三篇:计算机对称加密法)