TcpConnection对象生命周期
class TcpServer : noncopyable
{
public:
typedef std::function<void(EventLoop*)> ThreadInitCallback;
enum Option
{
kNoReusePort, //不复用端口
kReusePort, //复用端口
};
//TcpServer(EventLoop* loop, const InetAddress& listenAddr);
TcpServer(EventLoop* loop,
const InetAddress& listenAddr, //绑定端口和主机地址,并且提供单独的输出函数(InetAddress类)
const string& nameArg,
Option option = kNoReusePort);
~TcpServer(); // force out-line dtor, for std::unique_ptr members.
const string& ipPort() const { return ipPort_; }
const string& name() const { return name_; }
EventLoop* getLoop() const { return loop_; }
/// Set the number of threads for handling input.
///
/// Always accepts new connection in loop's thread.
/// Must be called before @c start
/// @param numThreads
/// - 0 means all I/O in loop's thread, no thread will created.
/// this is the default value.
/// - 1 means all I/O in another thread.
/// - N means a thread pool with N threads, new connections
/// are assigned on a round-robin basis.
void setThreadNum(int numThreads);
void setThreadInitCallback(const ThreadInitCallback& cb)
{ threadInitCallback_ = cb; }
/// valid after calling start()
std::shared_ptr<EventLoopThreadPool> threadPool()
{ return threadPool_; }
/// Starts the server if it's not listening.
///
/// It's harmless to call it multiple times.
/// Thread safe.
void start();
/// Set connection callback.
/// Not thread safe.
void setConnectionCallback(const ConnectionCallback& cb)
{ connectionCallback_ = cb; }
/// Set message callback.
/// Not thread safe.
void setMessageCallback(const MessageCallback& cb)
{ messageCallback_ = cb; }
/// Set write complete callback.
/// Not thread safe.
void setWriteCompleteCallback(const WriteCompleteCallback& cb)
{ writeCompleteCallback_ = cb; }
private:
/// Not thread safe, but in loop
void newConnection(int sockfd, const InetAddress& peerAddr);
/// Thread safe.
void removeConnection(const TcpConnectionPtr& conn);
/// Not thread safe, but in loop
void removeConnectionInLoop(const TcpConnectionPtr& conn);
typedef std::map<string, TcpConnectionPtr> ConnectionMap;
//每个线程对应一个EventLoop是怎样做到的
EventLoop* loop_; // the acceptor loop 主线程中的loop
const string ipPort_;
const string name_; //服务器名
std::unique_ptr<Acceptor> acceptor_; // avoid revealing Acceptor
std::shared_ptr<EventLoopThreadPool> threadPool_; //开启多线程EventLoop
ConnectionCallback connectionCallback_; //默认打印日志
MessageCallback messageCallback_; //默认重置buffer
WriteCompleteCallback writeCompleteCallback_; //写回调
ThreadInitCallback threadInitCallback_; //开启IO线程回调默认为空
AtomicInt32 started_;
// always in loop thread
int nextConnId_;
ConnectionMap connections_; //保存conn连接
};
构造函数
在该函数中我们可以看到acceptor_这个成员变量,该变量是主线程用于监听用户连接的
在函数中有一个Acceptor成员
TcpServer::TcpServer(EventLoop* loop,
const InetAddress& listenAddr,
const string& nameArg,
Option option)
: loop_(CHECK_NOTNULL(loop)), //用户传入的loop,也是主线的loop
ipPort_(listenAddr.toIpPort()),
name_(nameArg), //标识和生成唯一键值
acceptor_(new Acceptor(loop, listenAddr, option == kReusePort)),//acceptor中存在套接字,回调进行事件分发
threadPool_(new EventLoopThreadPool(loop, name_)),//IO线程池 每一个元素为一个reactor
connectionCallback_(defaultConnectionCallback),//默认为打印日志
messageCallback_(defaultMessageCallback),//默认重置buffer
nextConnId_(1) //标识和生成唯一键值
{
acceptor_->setNewConnectionCallback(
std::bind(&TcpServer::newConnection, this, _1, _2));
//新连接到来的时候会回调newConnection 产生一个TcpConnection对象
}
成功连接后的回调函数
void TcpServer::newConnection(int sockfd, const InetAddress& peerAddr)//peerAddr 客端的地址与协议
{
loop_->assertInLoopThread(); //是否为IO线程
EventLoop* ioLoop = threadPool_->getNextLoop(); //获取一个EventLoop对象
//事件分发机制
char buf[64];
snprintf(buf, sizeof buf, "-%s#%d", ipPort_.c_str(), nextConnId_);//ip+地址+当前连接的序号
++nextConnId_;
string connName = name_ + buf; //生成唯一的名字
LOG_INFO << "TcpServer::newConnection [" << name_
<< "] - new connection [" << connName
<< "] from " << peerAddr.toIpPort();
InetAddress localAddr(sockets::getLocalAddr(sockfd)); //获取客端的地址
// FIXME poll with zero timeout to double confirm the new connection
// FIXME use make_shared if necessary
TcpConnectionPtr conn(new TcpConnection(ioLoop,
connName,
sockfd,
localAddr,
peerAddr)); //创建一个TcpConnection
connections_[connName] = conn; //放入map 引用计数增加
conn->setConnectionCallback(connectionCallback_); //连接回调 默认为打印日志(如果用户未设置)
conn->setMessageCallback(messageCallback_); //信息回调 默认重置缓冲区
conn->setWriteCompleteCallback(writeCompleteCallback_); //写操作完成时触发的回调 默认为空
conn->setCloseCallback(
std::bind(&TcpServer::removeConnection, this, _1)); // FIXME: unsafe 断开连接回调
ioLoop->runInLoop(std::bind(&TcpConnection::connectEstablished, conn)); //加入loop事件循环 并延长conn的生命周期
}
这里我们主要讲下TcpConnection的生命周期(用户不持有的情况下)
typedef std::shared_ptr<TcpConnection> TcpConnectionPtr;
我们使用shared_ptr来维护TcpConnection,因为它的生命周期是很模糊的,此处以两个地方来讲述:
第一点
当Channel关注的事件发生后,在loop()循环中会把这些Channel捕获到并且依次回调它们关注事件的函数,我们知道
Channel的生命周期是由它的上层来管理的(这里单独指用户连接),也就是TcpConnection对象.如果此时Channel被放入待处理队列后TcpConnection对象被析构了,那么Channel也会被析构掉,此时程序就会core dump.
那么Muduo是怎么处理这个问题呢
void Channel::handleEvent(Timestamp receiveTime)
{
std::shared_ptr<void> guard; //在该函数执行完后,该指针指向的对象引用计数减少1
if (tied_) //判断是否有shared_ptr被接收,
{
guard = tie_.lock(); //提升为shared_ptr
if (guard)
{
handleEventWithGuard(receiveTime);
}
}
else //没有shared_ptr,则直接执行
{
handleEventWithGuard(receiveTime);
}
}
第一个if是用于判断这个Channel是否是TcpConnection对象的Chnnel 我们来讲明白这句话
先看下面这个函数
void Channel::tie(const std::shared_ptr<void>& obj)
{
tie_ = obj;
tied_ = true; //收到了TcpConnection传递的 shared_ptr
}
收到了TcpConnection传递的 shared_ptr ,那么我们什么时候传递的shared_ptr呢? 可以在newConnection()看到ioLoop->runInLoop(std::bind(&TcpConnection::connectEstablished, conn));
void TcpConnection::connectEstablished()
{
loop_->assertInLoopThread();
assert(state_ == kConnecting);
setState(kConnected);
channel_->tie(shared_from_this());//shared_from_this() 返回一个当前类的shared_ptr
channel_->enableReading();
connectionCallback_(shared_from_this());
}
好了,这里我们就知道了 tie_接收的就是 shared_from_this()返回指向 TcpConnection 的shared_ptr
void Channel::handleEvent(Timestamp receiveTime)
{
std::shared_ptr<void> guard; //在该函数执行完后,该指针指向的对象引用计数减少1
if (tied_) //判断是否有shared_ptr被接收,
{
guard = tie_.lock(); //提升为shared_ptr
if (guard)
{
handleEventWithGuard(receiveTime);
}
}
else //没有shared_ptr,则直接执行
{
handleEventWithGuard(receiveTime);
}
}
此时如果是TcpConnection的Channel被处理,那么它就会进入这个if,之后在通过 tie_.lock() 提升为shared_ptr(前提是这个weak_ptr观察的shared_ptr还是存在的),提升成功会增加 shared_ptr 的计数从而延长TcpConnection对象的生命周期,保证它能在执行回调时不会被析构
第二点
从TcpConnection的断开说起
void TcpServer::newConnection(int sockfd, const InetAddress& peerAddr)//peerAddr 客端的地址与协议
{
loop_->assertInLoopThread(); //是否为IO线程
EventLoop* ioLoop = threadPool_->getNextLoop(); //获取一个EventLoop对象
//事件分发机制
char buf[64];
snprintf(buf, sizeof buf, "-%s#%d", ipPort_.c_str(), nextConnId_);//ip+地址+当前连接的序号
++nextConnId_;
string connName = name_ + buf; //生成唯一的名字
LOG_INFO << "TcpServer::newConnection [" << name_
<< "] - new connection [" << connName
<< "] from " << peerAddr.toIpPort();
InetAddress localAddr(sockets::getLocalAddr(sockfd)); //获取客端的地址
// FIXME poll with zero timeout to double confirm the new connection
// FIXME use make_shared if necessary
TcpConnectionPtr conn(new TcpConnection(ioLoop,
connName,
sockfd,
localAddr,
peerAddr)); //创建一个TcpConnection
connections_[connName] = conn; //放入map 引用计数增加
conn->setConnectionCallback(connectionCallback_); //连接回调 默认为打印日志(如果用户未设置)
conn->setMessageCallback(messageCallback_); //信息回调 默认重置缓冲区
conn->setWriteCompleteCallback(writeCompleteCallback_); //写操作完成时触发的回调 默认为空
conn->setCloseCallback(
std::bind(&TcpServer::removeConnection, this, _1)); // FIXME: unsafe 断开连接回调
ioLoop->runInLoop(std::bind(&TcpConnection::connectEstablished, conn)); //加入loop事件循环 并延长conn的生命周期
}
我们从TcpConnection的 CloseCallback()开始说,那么我们需要知道什么时候调用这个 CloseCallback(),答案在
TcpConnection::handleRead
中
void TcpConnection::handleRead(Timestamp receiveTime)
{
loop_->assertInLoopThread();
int savedErrno = 0;
ssize_t n = inputBuffer_.readFd(channel_->fd(), &savedErrno); //读取数据到inputBuffer中
if (n > 0)
{
messageCallback_(shared_from_this(), &inputBuffer_, receiveTime); //调用用户设置的回调,并且把刚刚读取到信息的inputBuffer一起传递
}
else if (n == 0) //对端关闭
{
handleClose(); //此时Channel还是在执行handleEvent(),所以不能析构Channel
}
else
{
errno = savedErrno;
LOG_SYSERR << "TcpConnection::handleRead";
handleError();
}
}
void TcpConnection::handleClose()
{
loop_->assertInLoopThread();
LOG_TRACE << "fd = " << channel_->fd() << " state = " << stateToString();
assert(state_ == kConnected || state_ == kDisconnecting);
// we don't close fd, leave it to dtor, so we can find leaks easily.
setState(kDisconnected); //设置当前conn的状态
channel_->disableAll();
TcpConnectionPtr guardThis(shared_from_this()); //conn的计数 ++
connectionCallback_(guardThis);
// must be the last line
closeCallback_(guardThis);
}
closeCallback_(guardThis);
被设置为
conn->setCloseCallback(std::bind(&TcpServer::removeConnection, this, _1));
void TcpServer::removeConnection(const TcpConnectionPtr& conn)
{
// FIXME: unsafe
loop_->runInLoop(std::bind(&TcpServer::removeConnectionInLoop, this, conn));
}
void TcpServer::removeConnectionInLoop(const TcpConnectionPtr& conn)
{
loop_->assertInLoopThread();
LOG_INFO << "TcpServer::removeConnectionInLoop [" << name_
<< "] - connection " << conn->name();
size_t n = connections_.erase(conn->name()); //从map中删除 计数-1
(void)n;
assert(n == 1);
EventLoop* ioLoop = conn->getLoop();
/*
此处一定要用EventLoop::queueInLoop(),避免Channel对象被提前销毁
这里用std::bind让TcpConnection的生命期长到调用connectDestroyed()的时刻
使用std::bind得到一个boost::function对象,会把conn传递进去,引用计数会加1
*/
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(); //移除channel
//TcpConntion的生命周期在这个函数中才会结束(如果用户不持有TcpConnection对象)
}
此处一定要用EventLoop::queueInLoop(),避免Channel对象被提前销毁
为什么一定要用这个函数呢,因为runInLoop()会判断当前线程为IO线程就直接执行传入的cb了,那么connectDestroyed()就被调用,然后TcpConnection被析构,对应的Channel也被析构(如果此时的Channel在被处理队列中就会造成程序崩溃)
void EventLoop::runInLoop(Functor cb)
{
if (isInLoopThread()) //如果当前是IO线程,那么同步执行任务回调
{
cb();
}
else //否则放入队列中
{
queueInLoop(std::move(cb));
}
}
void EventLoop::queueInLoop(Functor cb)
{
{
MutexLockGuard lock(mutex_); //暴露给了其他线程需要上锁
pendingFunctors_.push_back(std::move(cb)); //加入队列
}
//此时只会执行进入这个临界区之前放入队列的任务,后续的任务等到下次循环再执行
if (!isInLoopThread() || callingPendingFunctors_)
{
wakeup(); //往wakefd_写入数据,唤醒IO multiplexing
}
}