TcpConnection类主要负责封装一次TCP连接,向Channel类注册回调函数(可读、可写、可关闭、错误处理),将来当Channel类上的事件发生时,调用相应的回调函数进行数据收发或者错误处理。
TcpConnection是使用shared_ptr来管理的类,因为它的生命周期模糊。TcpConnection表示已经建立或正在建立的连接,建立连接后,用户只需要在上层类如TcpServer中设置连接到来和消息到来的处理函数,继而回调TcpConnection中的 setConnectionCallback和setMessageCallback函数,实现对事件的处理。用户需要关心的事件是有限的,其他都由网络库负责。
TcpConnection中封装了InputBuffer和OutputBuffer,用来表示应用层的缓冲区。在发送数据时,如果不能一次将Buffer中的数据发送完毕,它还会继续关注Channel中的可写事件,当sockfd可写时,会再次发送。
#include <muduo/net/TcpConnection.h>
#include <muduo/base/Logging.h>
#include <muduo/base/WeakCallback.h>
#include <muduo/net/Channel.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/Socket.h>
#include <muduo/net/SocketsOps.h>
#include <errno.h>
using namespace muduo;
using namespace muduo::net;
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();
}
TcpConnection::TcpConnection(EventLoop* loop,
const string& nameArg,
int sockfd,
const InetAddress& localAddr,
const InetAddress& peerAddr)
: loop_(CHECK_NOTNULL(loop)),
name_(nameArg),
state_(kConnecting),
reading_(true),
socket_(new Socket(sockfd)),
channel_(new Channel(loop, sockfd)),
localAddr_(localAddr),
peerAddr_(peerAddr),
highWaterMark_(64*1024*1024)
{
// 向Channel对象注册可读事件
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));
LOG_DEBUG << "TcpConnection::ctor[" << name_ << "] at " << this << " fd=" << sockfd;
socket_->setKeepAlive(true);
}
TcpConnection::~TcpConnection()
{
LOG_DEBUG << "TcpConnection::dtor[" << name_ << "] at " << this
<< " fd=" << channel_->fd()
<< " state=" << stateToString();
assert(state_ == kDisconnected);
}
bool TcpConnection::getTcpInfo(struct tcp_info* tcpi) const
{
return socket_->getTcpInfo(tcpi);
}
string TcpConnection::getTcpInfoString() const
{
char buf[1024];
buf[0] = '\0';
socket_->getTcpInfoString(buf, sizeof buf);
return buf;
}
/********************************************************************
Modify : Eric Lee
Date : 2018-01-20
Description : 发数据是主动的,用户一般可用TcpConnection::send()
来发送数据,最终会调到TcpConnection::sendInLoop()。如果数据不能一次
发完,则打开channel的写事件开关,分开几次发。
*********************************************************************/
void TcpConnection::send(const void* data, int len)
{
send(StringPiece(static_cast<const char*>(data), len));
}
// 实际调用的是sendInLoop,在IO线程中去调用,保证安全性
void TcpConnection::send(const StringPiece& message)
{
if (state_ == kConnected)
{
if (loop_->isInLoopThread())
{
sendInLoop(message);
}
else
{
void (TcpConnection::*fp)(const StringPiece& message) = &TcpConnection::sendInLoop;
loop_->runInLoop(std::bind(fp, this, message.as_string()));
}
}
}
void TcpConnection::send(Buffer* buf)
{
if (state_ == kConnected)
{
if (loop_->isInLoopThread())
{
sendInLoop(buf->peek(), buf->readableBytes());
buf->retrieveAll();
}
else
{
void (TcpConnection::*fp)(const StringPiece& message) = &TcpConnection::sendInLoop;
loop_->runInLoop(std::bind(fp, this, buf->retrieveAllAsString()));
}
}
}
void TcpConnection::sendInLoop(const StringPiece& message)
{
sendInLoop(message.data(), message.size());
}
/********************************************************************
Modify : Eric Lee
Date : 2018-01-20
Description : 实际的发送数据函数。
*********************************************************************/
void TcpConnection::sendInLoop(const void* data, size_t len)
{
loop_->assertInLoopThread();
ssize_t nwrote = 0;
size_t remaining = len;
bool faultError = false;
if (state_ == kDisconnected)
{
LOG_WARN << "disconnected, give up writing";
return;
}
// 如果当前channel没有写事件发生,或者发送buffer已经清空,那么就不通过缓冲区直接发送数据
if (!channel_->isWriting() && outputBuffer_.readableBytes() == 0)
{
// 发送数据
nwrote = sockets::write(channel_->fd(), data, len);
if (nwrote >= 0)
{
// 记录下没发完的数据大小
remaining = len - nwrote;
// 如果发完了,回调写完成函数
if (remaining == 0 && writeCompleteCallback_)
{
loop_->queueInLoop(std::bind(writeCompleteCallback_, shared_from_this()));
}
}
// 异常处理
else
{
nwrote = 0;
if (errno != EWOULDBLOCK)
{
LOG_SYSERR << "TcpConnection::sendInLoop";
if (errno == EPIPE || errno == ECONNRESET)
{
faultError = true;
}
}
}
}
// 如果还有残留的数据没有发送完成
assert(remaining <= len);
if (!faultError && remaining > 0)
{
size_t oldLen = outputBuffer_.readableBytes();
// 如果输出缓冲区的数据已经超过高水位标记,那么调用highWaterMarkCallback_
if (oldLen + remaining >= highWaterMark_ && oldLen < highWaterMark_ && highWaterMarkCallback_)
{
loop_->queueInLoop(std::bind(highWaterMarkCallback_, shared_from_this(), oldLen + remaining));
}
// 把数据添加到输出缓冲区中
outputBuffer_.append(static_cast<const char*>(data) + nwrote, remaining);
// 监听channel的可写事件(因为还有数据未发完),
// 当可写事件被触发,就可以继续发送了,调用的是TcpConnection::handleWrite()
if (!channel_->isWriting())
{
channel_->enableWriting();
}
}
}
void TcpConnection::shutdown()
{
if (state_ == kConnected)
{
setState(kDisconnecting);
loop_->runInLoop(std::bind(&TcpConnection::shutdownInLoop, this));
}
}
/********************************************************************
Modify : Eric Lee
Date : 2018-01-20
Description : 主动关闭,调用TcpConnection::shutdown()。
*********************************************************************/
void TcpConnection::shutdownInLoop()
{
loop_->assertInLoopThread();
// 先判断是否发送buffer里还有东西,若有,暂时不关闭
if (!channel_->isWriting())
{
// 关闭写功能
socket_->shutdownWrite();
}
}
void TcpConnection::forceClose()
{
if (state_ == kConnected || state_ == kDisconnecting)
{
setState(kDisconnecting);
loop_->queueInLoop(std::bind(&TcpConnection::forceCloseInLoop, shared_from_this()));
}
}
void TcpConnection::forceCloseWithDelay(double seconds)
{
if (state_ == kConnected || state_ == kDisconnecting)
{
setState(kDisconnecting);
loop_->runAfter(seconds, makeWeakCallback(shared_from_this(), &TcpConnection::forceClose));
}
}
void TcpConnection::forceCloseInLoop()
{
loop_->assertInLoopThread();
if (state_ == kConnected || state_ == kDisconnecting)
{
handleClose();
}
}
const char* TcpConnection::stateToString() const
{
switch (state_)
{
case kDisconnected:
return "kDisconnected";
case kConnecting:
return "kConnecting";
case kConnected:
return "kConnected";
case kDisconnecting:
return "kDisconnecting";
default:
return "unknown state";
}
}
void TcpConnection::setTcpNoDelay(bool on)
{
socket_->setTcpNoDelay(on);
}
void TcpConnection::startRead()
{
loop_->runInLoop(std::bind(&TcpConnection::startReadInLoop, this));
}
void TcpConnection::startReadInLoop()
{
loop_->assertInLoopThread();
if (!reading_ || !channel_->isReading())
{
channel_->enableReading();
reading_ = true;
}
}
void TcpConnection::stopRead()
{
loop_->runInLoop(std::bind(&TcpConnection::stopReadInLoop, this));
}
void TcpConnection::stopReadInLoop()
{
loop_->assertInLoopThread();
if (reading_ || channel_->isReading())
{
channel_->disableReading();
reading_ = false;
}
}
/********************************************************************
Modify : Eric Lee
Date : 2018-01-20
Description : TcpConnection建立连接完成。
*********************************************************************/
void TcpConnection::connectEstablished()
{
loop_->assertInLoopThread();
assert(state_ == kConnecting); // 正处于连接建立过程
setState(kConnected);
channel_->tie(shared_from_this());
// 每个连接对应一个channel,打开描述符的可读属性
channel_->enableReading();
// 连接成功,回调客户注册的函数(由用户提供的函数,比如OnConnection)
connectionCallback_(shared_from_this());
}
/********************************************************************
Modify : Eric Lee
Date : 2018-01-20
Description : TcpConnection::connectDestroyed()是TcpConnection析构前
最后调用的一个函数,它通知用户连接已断开。
*********************************************************************/
void TcpConnection::connectDestroyed()
{
loop_->assertInLoopThread();
if (state_ == kConnected)
{
setState(kDisconnected);
channel_->disableAll();
// 仅仅起到记录作用而已
connectionCallback_(shared_from_this());
}
// 在poller中移除channel
channel_->remove();
}
/********************************************************************
Modify : Eric Lee
Date : 2018-01-20
Description :
当某个channel有读事件发生时,会调用TcpConnection::handleRead()函数,
然后从socket里读入数据到buffer,再通过回调把这些数据返回给用户层。
*********************************************************************/
void TcpConnection::handleRead(Timestamp receiveTime)
{
loop_->assertInLoopThread(); // 断言是否在loop线程
int savedErrno = 0;
// 读数据到inputBuffer_中
ssize_t n = inputBuffer_.readFd(channel_->fd(), &savedErrno);
if (n > 0)
{
// 用户提供的处理信息的回调函数(由用户自己提供的函数,比如OnMessage)
messageCallback_(shared_from_this(), &inputBuffer_, receiveTime);
}
// 读到了0,表明客户端已经关闭了
else if (n == 0)
{
handleClose();
}
else
{
errno = savedErrno;
LOG_SYSERR << "TcpConnection::handleRead";
handleError();
}
}
/********************************************************************
Modify : Eric Lee
Date : 2018-01-20
Description : 当可写事件触发时,调用TcpConnection::handleWrite()。
*********************************************************************/
void TcpConnection::handleWrite()
{
loop_->assertInLoopThread();
if (channel_->isWriting())
{
// 写数据
ssize_t n = sockets::write(channel_->fd(), outputBuffer_.peek(), outputBuffer_.readableBytes());
if (n > 0)
{
// 调整发送buffer的内部index,以便下次继续发送
outputBuffer_.retrieve(n);
// 如果可读的数据量为0,这里的可读是针对系统发送函数来说的,不是针对用户
// 如果对于系统发送函数来说,可读的数据量为0,表示所有数据都被发送完毕了,即写完成了
if (outputBuffer_.readableBytes() == 0)
{
// 不再关注写事件
channel_->disableWriting();
if (writeCompleteCallback_)
{
// 调用用户的写完成回调函数
loop_->queueInLoop(std::bind(writeCompleteCallback_, shared_from_this()));
}
// 如果当前状态是正在关闭连接,那么就调用shutdown来主动关闭连接
if (state_ == kDisconnecting)
{
shutdownInLoop();
}
}
}
else
{
LOG_SYSERR << "TcpConnection::handleWrite";
}
}
else
{
LOG_TRACE << "Connection fd = " << channel_->fd() << " is down, no more writing";
}
}
/********************************************************************
Modify : Eric Lee
Date : 2018-01-20
Description : 当对端调用shutdown()关闭连接时,本端会收到一个FIN,
channel的读事件被触发,但inputBuffer_.readFd() 会返回0,然后调用
handleClose(),处理关闭事件,最后调用TcpServer::removeConnection()。
*********************************************************************/
void TcpConnection::handleClose()
{
// 断言是否在loop线程
loop_->assertInLoopThread();
LOG_TRACE << "fd = " << channel_->fd() << " state = " << stateToString();
assert(state_ == kConnected || state_ == kDisconnecting);
setState(kDisconnected);
// channel上不再关注任何事情
channel_->disableAll();
// 获得shared_ptr交由tcpsever处理
TcpConnectionPtr guardThis(shared_from_this());
// 作用是记录一些日志
connectionCallback_(guardThis);
// 回调TcpServer::removeConnection(),TcpConnection的生命期由TcpServer控制
closeCallback_(guardThis);
}
/********************************************************************
Modify : Eric Lee
Date : 2018-01-20
Description : 处理出错事件
*********************************************************************/
void TcpConnection::handleError()
{
int err = sockets::getSocketError(channel_->fd());
LOG_ERROR << "TcpConnection::handleError [" << name_
<< "] - SO_ERROR = " << err << " " << strerror_tl(err);
}