,有一个readerIndex和writerIndex分别表示可读位置和可写位置,这两个位置内的区间表示Buffer已有的数据。值得注意的是Buffer的两个trick:每次读取fd上的数据时通过readv一部分读取到Buffer中,一部分读取到找空间char extrabuf[65535]中,若Buffer装满了即extrabuf中有数据,则需要extrabuf中的数据append到Buffer中,这样做可以在初始时每个连接的Buffer的避免过大造成内存浪费,也避免反复调用read的系统开销,每次Buffer不够时通过extrabuf再append到Buffer中使Buffer慢慢变大;还有一个就是Buffer提供了一个前向空间,在消息序列化完毕时可以通过prepend()将消息的大小添加的Buffer的头部。另外Buffer的readerIndex和writerIndex都是移动的,只要在Buffer的空闲空间不够时才加大Buffer的vector,否则可以通过内部腾挪方式即让readerIndex和writerIndex向前移动(数据也跟着移动)这样Buffer就不增大vector.size().
12
class TcpServe的改进: TcpServer有自己的一个EventLoop用来接收新的TCP客户连接,然后从event loop pool中选一个loop给TCP客户连接(即TcpConnection)。这就需要使用class EventLoopThreadPool来创建多个线程每个线程一个EventLoop(one loop per thread)。
TcpServer::newConnection()在创建一个TcpConnection后从EventLoopTreadPool中选一个EventLoop给这个TcpConnection。
TcpServer::removeConnection()需要拆分成两个函数,因为现在TcpServer和TcpConenction可能不再同一个线程里了,需要通过线程转移函数将移除操作转移到TcpConneciton的IO线程中去。TcpServer::removeConnection()->EventLoop::runInLoop()->TcpServer::removeConnectionInLoop()->EventLoop::runInLoop()->TcpConnection::connectDestroyed()。Tcpserver中erase掉这个TcpConnectionPtr。
13 class Connector:用于发起连接,当socket变得可写时表示连接建立完毕,其间需要处理各种类型的错误。connect返回EAGAIN是真的错误表示暂时没有端口可用,要关闭socket稍后再试;EINPROGRESS是“正在连接”,即使socket可写,也需要用getsockopt(sokfd,SOL_SOCKET,SO_ERROR...)再次确认。超时重连的时间应逐渐延长,需要处理自连接的情况。Connector只负责建立连接,不负责建立TcpConnection,它有一个建立连接回调函数由用户指定(Connector基本是TCP客户端使用,且一个客户端一个Conenctor)。
Connector有三个状态:kDisconnected未连接,kConnecting正在连接,kConnected已连接。
Connector构造时指定一个服务端地址InetAddress和一个事件循环EventLoop,状态设为kDisConnected。指定一个最大重试连接时间。
Connector::start()可以由其它线程调用,该函数内部执行EventLoop::runInLoop(bind(&Connector::startInLoop,this))将通过EventLoop将操作转移到Connector的线程中去。
Connector::startInLoop()判断此Connector还没有连接,则调用Connector::connect()
Connector::connect()创建一个sockfd,并调用connect(sockfd,&serverAddress,sizeof(serverAddress)),然后返回一个errno,根据errno的值进行进一步选择是关闭连接,重师连接还是调用已连接函数。
连接返回errno为EAGAIN则调用Connector::retry()该函数调用EventLoop::runAfter()在一段时间间隔后重试连接。
errno为EINPROGRESS表示正在连接,则调用Connector::connecting()该函数将该为该连接设置一个Channel来管理连接,并向Channel注册Connector::handleWrite,Conenctor::handleError的回调函数。其中Connector::handleWrite()当正在连接kConnecting则用需要测试该连接(已经可写了还kConencting)可能需要进一步重新连接即调用Connector::retry()注意这时候socketfd需要重新分配了,而Conenctor是可以重复使用的。Cionnector::handleError()执行Connector::removeAndResetChannel()和Connector::retry()重试连接。
当用户或其它线程调用Connector::start()->EventLoop::runInLoop()->Connector::startInLoop()->Connector::connect()根据errnor执行下面的情形:
EAGAIN:Connector::retry()->EventLoop::runAfter()延迟重连->Conncetor::startInLoop()
EACCESS/EPERM/EBADF: close(sockfd)
EINPROGRESS:Connector::connecting()该函数向Channel注册回调函数并Channel::enableWriting()关注这个正在连接的socketfd是否可写。
此后当处于“正在连接kConnecting”的sockfd事件就绪时:Channel::handelEvent()->Connector::handleWrite()/Connector::handleError()
Connector::handleWrite()若检测Connector状态仍在kConnecting时需要调用getsocketopt(sockfd,SOL_SOCKET,SO_ERROR...)检测,若返回0则执行用户指定的连接回调函数Connector::newConnectionCallback()。
Connector::handleError()->Conenctor::retry()
14 class TcpClient:每个TcpClinet只管理一个Connector。其内容和TcpServer差不多,TcpClient具备TcpConnection断开连接后重新连接的功能。
TcpClient构造时new一个Connector,并指定一个EventLoop。用户要指定的connectionCallback,messageCallback回调函数。并设置Connector::setNewConnection(bind(&TcpClient::newConnection,this,_1))将连接建立回调赋给Connector::newConnectionCallback。
用户调用TcpClient::connect()发起连接->Conenctor::start()->TcpClient::newConnection().
TcpClinet::newConnection()将new一个TcpConneciton对象conn并设置TcpConnection::setConnecitonCallback/setMessageCallback/setWriteCompleteCallback/setCloseCallback等回调函数。其中setConnectionCallback将用户指定的ConnectionCallback传给TcpConenciton,setMessageCallback将TcpClient中用户指定的messageCallback传给TcpConneciton。TcpConnection::closeCallback是TcpClient::removeConenction。最后TcpClient::newConneciton()将会调用TcpConnection::connectEstablished()。
TcpClient::removeConnection(),由于TcpClient只管理一个Connector也就是一个TcpConenction它们都在一个线程中,所以不涉及操作线程转移。TcpClient::removeConenction()->EventLoop::queueInLoop()->TcpConnection::connectDestroyed().若有重连的必要将执行Connector::restart()。
14 class Epoller和Poller差不多。
代码部分:
注意这里的代码没有muduo网络库全,挂在这里只是做了些注释,算是留个尸体吧。如有必要请参看muduo。
#include
#include