muduo学习笔记-Acceptor类
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类相关推荐
- muduo学习笔记 日志类
learn_muduo Logger Logger有六个日志等级 TRACE DEBUG INFO WARN ERROR FATAL 日志的输出语句是通过宏定义完成,编译期完成宏定义替换,创建Logg ...
- muduo学习笔记 线程类
learn_muduo 线程属性 线程标识 pthreadId_,pid_t 线程函数 func_ 线程名字 name_ 线程序号 numCreated_ bool started_; // 线程状态 ...
- muduo学习笔记:net部分之Http--HttpServer
前面[muduo学习笔记:net部分之Http–HttpRequest.HttpResponse 和 HttpContext]介绍了TCP数据数据Buffer承载的HTTP报文的解析,本文结合TcpS ...
- ASM学习笔记2 - 类的创建和修改 —— ClassWriter的综合应用
ASM学习笔记2 - 类的创建和修改 -- ClassWriter的综合应用 上回我们说到,通过使用ClassVisitor和ClassReader,我们能够分析已经存在的类.这一节中,我们将使用Cl ...
- Python学习笔记 (类与对象)
Python学习笔记 (类与对象) 1.类与对象 面向对象编程语言类: 一个模板, (人类)-是一个抽象的, 没有实体的对象: (eg: 张三, 李四) 属性: (表示这类东西的特征, 眼睛, 嘴巴, ...
- Machine Learning A-Z学习笔记12-分类模型性能评级及选择
Machine Learning A-Z学习笔记12-分类模型性能评级及选择 1.简单原理 一般认为假阴性比假阳性更严重,如核酸检测 用混淆矩阵表示如下图 准确率驳论(Accuracy Paradox ...
- JAVA学习笔记(类的学习)
JAVA学习笔记(类的学习) 类声明和类体 构造方法和对象创建 对象的引用和实体 成员变量 方法 方法重载 关键字this 包 import语句 访问权限 对象数组 反编译和文档生成器 JAR文件 1 ...
- python面向对象编程72讲_2020-07-22 Python学习笔记27类和面向对象编程
一些关于自己学习Python的经历的内容,遇到的问题和思考等,方便以后查询和复习. 声明:本人学习是在扇贝编程通过网络学习的,相关的知识.案例来源于扇贝编程.如果使用请说明来源. 第27关 类与面向对 ...
- 设计模式学习笔记1——类与类之间的关系
1.继承关系 概念 继承又分为2种方式,一种叫实现继承,一种叫接口继承.参见笔记--[2种继承方式学习笔记].(https://blog.csdn.net/yhb1206/article/detail ...
最新文章
- 8.1shell介绍 8.2命令历史 8.3命令补全和别名 8.4通配符 8.5输入输出重定向
- 中国研制600公里时速磁悬浮:北京到上海,仅需3.5小时左右
- 考前自学系列·计算机组成原理·微程序微指令微命令微操作
- linux+向进城发送信号,信号 - it610.com
- UVA - 572 Oil Deposits-dfs找连通块
- Python 第三方模块之 psutil - 获取系统运行的进程和系统利用率信息
- android 表情,软键盘冲突解决方案(仿微博等SNS应用)
- EFCore-一对一配置外键小记
- 【C语言】qsort函数用法(转)
- 数据挖掘-朴素贝叶斯分类
- ApacheCN Java 译文集 20210921 更新
- PRIMARY KEY 与 UNIQUE
- Contrastive Loss (对比损失)
- python unit test什么意思_python中的unittest有什么作用
- 供应链金融与区块链02——论文阅读
- 浅谈如何保障服务器安全
- 图片去黑底原理(做个笔记)
- linux中c语言kbhit函数用法,检测按键(Linux中kbhit()函数的实现)
- 用Scrapy爬取笔趣阁小说
- python中random模块