callback¶
call in Channel¶
在Channel中的回调函数如下:
ReadEventCallback readCallback_; // 可读事件到来时
EventCallback writeCallback_; // 可写事件到来时
EventCallback closeCallback_; // 关闭连接事件
EventCallback errorCallback_; // 发生错误事件
Channel负责关注I/O事件,并且设置对应的事件回调函数。在muduo中用到的I/O有timerfd、eventfd、socket。关于socket的事件回调函数在TcpConnection中实现,具体如下:
channel_->setReadCallback( std::bind(&TcpConnection::handleRead, this, _1));
channel_->setWriteCallback(std::bind(&TcpConnection::handleWrite, this));
channel_->setCloseCallback(std::bind(&TcpConnection::handleClose, this));
channel_->setErrorCallback(std::bind(&TcpConnection::handleError, this));
setReadCallback:如果不关注这个事件,数据自然无法读取
+ setWriteCallback:如果不关注这个,数据仅仅可以发送一次。
+ setCloseCallback:
callback in TcpConnection¶
ConnectionCallback connectionCallback_;
MessageCallback messageCallback_; // 有数据可读时。handleRead()内部
WriteCompleteCallback writeCompleteCallback_; // 数据写完时调用,handleWrite()、sendInLoop内部
HighWaterMarkCallback highWaterMarkCallback_; // 发送数据时用, sendInLoop 中
CloseCallback closeCallback_;
handleWrite()¶
在handleWrite()中需要使用highWaterMarkCallback_是因为,handleWrite()发送的是outputBuffer_中的数据,整个写的过程如下:
1. send() →sendInLoop() → 先将要发送的数据尽可能多地填充满socket缓冲区
2. 如果socket不足以容纳全部的待发送数据,那么此时注册POLLOUT事件
3. 等到socket将其缓冲区的数据发送出去,其缓冲区为空,触发POLLOUT事件
4. 这时候触发handleWrite()事件,将outputBuffer_中的数据发送出去
5. 将outputBuffer_中数据发送完,就再次调用writeCompleteCallback_,那么在其内部肯定有send(...)
6. 如果第一步中,待发送的数据能够全部复制到socket缓冲区,那么就直接发送,不会触发handleWrite(),直接调用writeCompleteCallback_,在其内部调用send。
7. 重复上述步骤。
因此,如果没有关注Channel中的handleWrite(),只要待发送的数据量大于socket的缓冲区,那么此后数据将无法再次发送。
handleRead()¶
对于messageCallback_是用在handleRead()中,为的是将socket中的数据提取到inputBuffer_中。而handleRead()如果没有关注:
+ 数据无法提取:那么epoll将一致通知用户去取,但是没有注册handleRead()函数,就会处于busy-loop
+ 无法应对peer的关闭操作,即无法被动关闭与某个客户端的连接,只能主动关闭。
+ 无法处理错误
因此,在messageCallback_中应该要有获取inputBuffer_中的数据的操作,默认的messageCallback_:
void muduo::net::defaultMessageCallback(const TcpConnectionPtr&, Buffer* buf, Timestamp) {
buf->retrieveAll();
}
readerIndex_和writerIndex_到初始化位置,没有读取数据,相当于丢弃了数据。
handClose()¶
handleClose()搭配closeCallback_,在handleClose()中调用closeCallback_,而是由TcpServer的内部函数TcpServer::removeConnectionInLoop实现。当检测到有客户端关闭时,就会触发,关闭与客户端的连接。
+ 对端关闭时,是触发handleRead()还是触发handClose()?
// 只是写了核心部分
void Channel::handleEventWithGuard(Timestamp receiveTime) {
if ((revents_ & POLLHUP) && !(revents_ & POLLIN)) {
if (logHup_) {
LOG_WARN << "fd = " << fd_ << " Channel::handle_event() POLLHUP";
}
if (closeCallback_) closeCallback_();
}
if (revents_ & (POLLIN | POLLPRI | POLLRDHUP)) {
if (readCallback_) readCallback_(receiveTime);
}
}
epoll的POLLIN操作,是通过返回的标志位不同,来判断是直接调用closeCallback_,还是调用readCallback_中的closeCallback_。+ 注意: +
Channel中的closeBack_与TcpConnection中的closeCallback_只是表示一致,但是含义不同。
+ TcpConnection中的closeCallback_由TcpServer传入,而channel_的即handleClose()。
callback in TcpServer¶
ConnectionCallback connectionCallback_;
MessageCallback messageCallback_;
WriteCompleteCallback writeCompleteCallback_;
TcpConnection中的五个回调函数都应该是由TcpServer传入,但是实际上muduo只是设置了四个,而highWaterMarkCallback_没有设置。
void TcpServer::newConnection(int sockfd, const InetAddress& peerAddr) {
...
connections_[connName] = conn; /** 引用计数增加1,变成2*/
conn->setConnectionCallback(connectionCallback_);
conn->setMessageCallback(messageCallback_);
conn->setWriteCompleteCallback(writeCompleteCallback_);
conn->setCloseCallback(std::bind(&TcpServer::removeConnection, this, _1));
...
}
void muduo::net::defaultConnectionCallback(const TcpConnectionPtr& conn) {
LOG_TRACE << conn->localAddress().toIpPort() << " -> "
<< conn->peerAddress().toIpPort() << " is "
<< (conn->connected() ? "UP" : "DOWN");
}
void muduo::net::defaultMessageCallback(const TcpConnectionPtr&, Buffer* buf, Timestamp) {
buf->retrieveAll();
}
WriteCompleteCallback_需要自己从外部传入,其实highWaterMarkCallback_也需要自己从外部传入,但是muduo在建立新连接时没有设置。
conn->setHighWaterMarkCallback(highWaterMarkCallback_);
至此,socketI/O中的回调函数已经基本全部分析完毕。其余的两个timerfd、eventfd对应的回调函数见第一个总结。