Muduo源码笔记系列:
muduo源码阅读笔记(2、对C语言原生的线程安全以及同步的API的封装)
muduo源码阅读笔记(6、ExevntLoop和Thread)
muduo源码阅读笔记(7、EventLoopThreadPool)
前言
本章涉及的文件有:
TcpServer.h/cc:一个主从Reactor模型的TcpServer,主EventLoop接收连接,并且将连接sock fd负载均衡分发给一个IOLoop。
Acceptor.h/cc:一个监听套接字的包装器,内部创建了一个Channel管理连接套接字的回调。
Socket.h/cc:封装原生socket,提供绑定、监听、接受连接、设置socket属性等接口。
SocketsOps.h/cc:Socket.h/cc接口的底层实现,在创建套接字(::socket()/::accept())时,会将socketfd设置为非阻塞。
InetAddress.h/cc:对sockaddr_in/sockaddr_in6网络地址进行封装,使其更方便使用。
本章重点集中在1、2,对于3、4、5,见名知意即可,感兴趣的读者,可以自行深入阅读。
Acceptor的实现
提供的接口:
1 | /// |
其实从成员变量就可以看出来,Acceptor和TimeQueue有着及其相似的地方。
实现的伪代码:
1 | Acceptor::Acceptor(EventLoop* loop, const InetAddress& listenAddr, bool reuseport) |
细节明细:
疑问
Acceptor::idleFd_成员变量存在的意义?Acceptor::handleRead函数中为什么出现EMFILE错误时,关闭idleFd_,::accept接受连接,又关闭idleFd_,又打开idleFd_?
解答
出现EMFILE错误,关闭 idleFd_ 后,执行 ::accept 操作,通常情况下这个操作并不会失败。这是因为在 ::accept 函数成功返回时,会返回一个值和关闭idleFd_前值相同的连接文件描述符,这个文件描述符会被用于处理新连接。这里主要目的是消耗一个连接,尽管什么也不做。但可以确保服务器能够继续正常运行。
TcpServer的实现
提供的接口:
1 | /// |
简单画了一下TcpServer整体架构图:
整体流程就是:
客户端发送连接请求。
listen套接字所在的EventLoop(假设为base loop)接受连接请求并创建io套接字。
base loop 通过EventLoopThreadPool的负载均衡算法选择一个io EventLoop,将io套接字传给该loop。
客户端和指定的loop进行TCP通信。
实现伪代码:
1 |
|
这里简单备忘一下
连接建立的回调过程:
base loop中listen套接字触发可读事件,调用Acceptor::handleRead函数处理事件(由Acceptor::acceptChannel_注册)
调用::accept接受连接并创建sockfd。将sockfd作为参数,调用Acceptor::newConnectionCallback_连接分发回调,也即TcpServer::newConnection(由TcpServer构造函数设置)
利用负载均衡算法,选择一个合适的ioloop,然后为连接拼接一个唯一的connect name,并用sockfd、ioloop等创建一个TcpConnection对象(智能指针),设置好回调。将 <key : connect name, value : TcpConnection> 作为TcpServer::connections_的一个记录(TcpConnection对象引用计数加一)。最后向ioloop的任务队列中添加一项回调任务:TcpConnection::connectEstablished,并将TcpConnection对象作为回调任务的参数。
执行TcpConnection::connectEstablished,连接建立。
连接拆除的回调过程:
调用TcpConnection::closeCallback_回调,即TcpServer::removeConnection,传入TcpConnection对象(引用)作为参数。
向base loop任务队列添加一项回调任务:TcpServer::removeConnectionInLoop,传入TcpConnection对象(引用)作为参数。
执行TcpServer::removeConnectionInLoop:通过TcpConnection对象的name,在TcpServer::connections_上删除连接记录(智能指针引用计数减一),然后向TcpConnection对象的ioloop的任务队列添加一项回调任务:TcpConnection::connectDestroyed,同样以TcpConnection对象作为回调任务的参数。
本章完结