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_);
至此,socket
I/O中的回调函数已经基本全部分析完毕。其余的两个timerfd
、eventfd
对应的回调函数见第一个总结。