每接受到一个连接,就会创建一个TcpConnection,每个TcpConnection用shared指针保存在TcpServer中,处理的回调都经过该类,这里最需要注意的就是关于TcpConnection的生命周期。
成员变量:
private:
EventLoop* loop_;
const string name_;
StateE state_;
std::unique_ptr<Socket> socket_;
std::unique_ptr<Channel> channel_;
const InetAddress localAddr_;
const InetAddress peerAddr_;
ConnectionCallback connectionCallback_;
MessageCallback messageCallback_;
WriteCompleteCallback writeCompleteCallback_;
HighWaterMarkCallback highWaterMarkCallback_;
CloseCallback closeCallback_;
size_t highWaterMark_;
Buffer inputBuffer_;
Buffer outputBuffer_;
loop是TcpConnection所在的循环,name是创建时传入的连接的一些信息,state_是TcpConnection当前状态机,有连接中,断开连接中,已连接和已断开4个状态,socket是传入的文件描述符,channel是创建的通道,里面含有一个TcpConnection的一个弱指针,适当的时候提升(Channel那期博客讲过),localAddr和peerAddr是对端和本端的地址信息,接下来就是几个回调函数,最后是输入和输出缓冲区Buff。
重要函数及作用:
创建
TcpConnection::TcpConnection(EventLoop* loop, const string& nameArg, int sockfd, const InetAddress& localAddr, const InetAddress& peerAddr)
: loop_(loop),
name_(nameArg),
state_(kConnecting),
socket_(new Socket(sockfd)),
channel_(new Channel(loop, sockfd)),
localAddr_(localAddr),
peerAddr_(peerAddr),
highWaterMark_(64 * 1024 * 1024)
{
channel_->setReadCallback(std::bind(&TcpConnection::handleRead, this, std::placeholders::_1));
channel_->setWriteCallback(std::bind(&TcpConnection::handleWrite, this));
channel_->setCloseCallback(std::bind(&TcpConnection::handleClose, this));
channel_->setErrorCallback(std::bind(&TcpConnection::handleError, this));
LOGD("TcpConnection::ctor[%s] at 0x%x fd=%d", name_.c_str(), this, sockfd);
socket_->setKeepAlive(true);
}
void TcpServer::newConnection(int sockfd, const InetAddress& peerAddr)
{
loop_->assertInLoopThread();
EventLoop* ioLoop = eventLoopThreadPool_->getNextLoop();
char buf[32];
snprintf(buf, sizeof buf, ":%s#%d", hostport_.c_str(), nextConnId_);
++nextConnId_;
string connName = name_ + buf;
LOGD("TcpServer::newConnection [%s] - new connection [%s] from %s", name_.c_str(), connName.c_str(), peerAddr.toIpPort().c_str());
InetAddress localAddr(sockets::getLocalAddr(sockfd));
TcpConnectionPtr conn(new TcpConnection(ioLoop, connName, sockfd, localAddr, peerAddr));
//放入map容器管理
connections_[connName] = conn;
conn->setConnectionCallback(connectionCallback_);//指向业务层Session
conn->setMessageCallback(messageCallback_);
conn->setWriteCompleteCallback(writeCompleteCallback_);
conn->setCloseCallback(std::bind(&TcpServer::removeConnection, this, std::placeholders::_1)); // FIXME: unsafe
//该线程分离完io事件后,立即调用TcpConnection::connectEstablished
ioLoop->runInLoop(std::bind(&TcpConnection::connectEstablished, conn));
}
当Acceptor内通道检测到新连接事件最终回调到TcpServer的newConnection函数,参数还有对端的地址信息以及网络文件描述符,之后从线程池中获得一个线程的EventLoop,创建一个TcpConnection,可以看到在构造函数中传入eventloop,名称信息,文件描述符,对端,本端地址信息,并在构造函数中创建Channel,设置channel事件处理的回调。创建完毕,再向TcpConnection中传入到各个类的回调函数,最后在对应IO线程中调用connectEstablished。
读事件处理
void TcpConnection::handleRead(Timestamp receiveTime)
{
loop_->assertInLoopThread();
int savedErrno = 0;
int32_t n = inputBuffer_.readFd(channel_->fd(), &savedErrno);
if (n > 0)
{
//messageCallback_指向CTcpSession::OnRead(const std::shared_ptr<TcpConnection>& conn, Buffer* pBuffer, Timestamp receiveTime)
messageCallback_(shared_from_this(), &inputBuffer_, receiveTime);
}
else if (n == 0)
{
handleClose();
}
else
{
errno = savedErrno;
LOGSYSE("TcpConnection::handleRead");
handleError();
}
}
由IO多路复用检测到读事件,随后通过Channel回调到TcpConnection::handleRead,之后再回调到我们用户设置的业务层Session的某个函数进行处理。
写事件处理
void TcpConnection::sendInLoop(const void* data, size_t len)
{
loop_->assertInLoopThread();
int32_t nwrote = 0;
size_t remaining = len;
bool faultError = false;
if (state_ == kDisconnected)
{
LOGW("disconnected, give up writing");
return;
}
// if no thing in output queue, try writing directly
if (!channel_->isWriting() && outputBuffer_.readableBytes() == 0)
{
nwrote = sockets::write(channel_->fd(), data, len);
//TODO: 打印threadid用于调试,后面去掉
//std::stringstream ss;
//ss << std::this_thread::get_id();
//LOGI << "send data in threadID = " << ss;
if (nwrote >= 0)
{
remaining = len - nwrote;
if (remaining == 0 && writeCompleteCallback_)
{
loop_->queueInLoop(std::bind(writeCompleteCallback_, shared_from_this()));
}
}
else // nwrote < 0
{
nwrote = 0;
if (errno != EWOULDBLOCK)
{
LOGSYSE("TcpConnection::sendInLoop");
if (errno == EPIPE || errno == ECONNRESET) // FIXME: any others?
{
faultError = true;
}
}
}
}
//assert(remaining <= len);
if (remaining > len)
return;
if (!faultError && remaining > 0)
{
size_t oldLen = outputBuffer_.readableBytes();
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);
if (!channel_->isWriting())
{
channel_->enableWriting();
}
}
}
void TcpConnection::handleWrite()
{
loop_->assertInLoopThread();
if (channel_->isWriting())
{
int32_t n = sockets::write(channel_->fd(), outputBuffer_.peek(), outputBuffer_.readableBytes());
if (n > 0)
{
outputBuffer_.retrieve(n);
if (outputBuffer_.readableBytes() == 0)
{
channel_->disableWriting();
if (writeCompleteCallback_)
{
loop_->queueInLoop(std::bind(writeCompleteCallback_, shared_from_this()));
}
if (state_ == kDisconnecting)
{
shutdownInLoop();
}
}
}
else
{
LOGSYSE("TcpConnection::handleWrite");
// if (state_ == kDisconnecting)
// {
// shutdownInLoop();
// }
//added by zhangyl 2019.05.06
handleClose();
}
}
else
{
LOGD("Connection fd = %d is down, no more writing", channel_->fd());
}
}
在类中重载了几个send()函数,实际上都会调用sendInLoop函数,那我们来看看sendInLoop这个函数。首先如果channel设置isWiriting或者发送缓冲区没有数据,那么将会直接调用write()。接下来会出现两种情况,一个是全部拷贝到了内核缓冲区,那么发送完毕,直接调用发送完成回调函数;二是遇到一些未发送或者未发送完全那么会压入发送缓冲区,如果超过缓冲区,则会触发高水位回调函数,采取一些措施,可能会关闭连接。压入缓冲区之后,然后使当前通道重新关注可写事件,当可写的时候,直接触发写回调,再写入缓冲区的字节。
关闭连接事件处理:
void TcpConnection::handleClose()
{
if (state_ == kDisconnected)
return;
loop_->assertInLoopThread();
LOGD("fd = %d state = %s", channel_->fd(), stateToString());
//assert(state_ == kConnected || state_ == kDisconnecting);
// we don't close fd, leave it to dtor, so we can find leaks easily.
setState(kDisconnected);
channel_->disableAll();
TcpConnectionPtr guardThis(shared_from_this());
connectionCallback_(guardThis); //打印
// must be the last line
closeCallback_(guardThis);
}
void TcpServer::removeConnection(const TcpConnectionPtr& conn)
{
// FIXME: unsafe
loop_->runInLoop(std::bind(&TcpServer::removeConnectionInLoop, this, conn));
}
void TcpServer::removeConnectionInLoop(const TcpConnectionPtr& conn)
{
loop_->assertInLoopThread();
LOGD("TcpServer::removeConnectionInLoop [%s] - connection %s", name_.c_str(), conn->name().c_str());
size_t n = connections_.erase(conn->name());
if (n != 1)
{
//出现这种情况,是TcpConneaction对象在创建过程中,对方就断开连接了。
LOGD("TcpServer::removeConnectionInLoop [%s] - connection %s, connection does not exist.", name_.c_str(), conn->name().c_str());
return;
}
EventLoop* ioLoop = conn->getLoop();
ioLoop->queueInLoop(std::bind(&TcpConnection::connectDestroyed, conn));
}
void TcpConnection::connectDestroyed()
{
loop_->assertInLoopThread();
if (state_ == kConnected)
{
setState(kDisconnected);
channel_->disableAll();
connectionCallback_(shared_from_this());
}
channel_->remove();
}
关闭连接事件很重要,涉及到TcpConnection和Channel的生命周期以及是否能合理销毁,用了智能指针来管理和控制生命周期。下面我们就来分析一下断开流程中TcpConnection的引用计数问题:
1.首先连接到来创建TcpConnection,并存入容器。--------------------------------------------------------------------------引用计数+1 总数:1
2.客户端断开连接,在Channel的handleEvent函数中会将Channel中的TcpConnection弱指针提升--------------引用计数+1 总数:2
3.触发HandleRead ,可读字节0,进而触发HandleClose,HandleClose函数中栈上的TcpConnectionPtr guardThis会继续将------------------------------------------------------------------------------------------------------------------------------------------------------引用计数+1 总数:3
4.触发HandleClose的回调函数 在TcpServer::removeConnection结束后(回归主线程队列),释放HandleClose的栈指针,以及Channel里提升的指针----------------------------------------------------------------------------------------------------------------引用计数-2 总数:1
5.主线程执行回调removeConnectionInLoop,在函数内部将tcpconnection从TcpServer中保存连接容器中erase掉。但在removeConnectionInLoop结尾用conn为参数构造了bind。---------------------------------------------------------------引用计数不变 总数:1
6.回归次线程处理connectDestroyed事件,结束完释放参数传递的最后一个shard_ptr,释放TcpConnection。------引用计数-1 总数:0