TcpConnection是使用shared_ptr来管理的类,由于它的生命周期模糊。TcpConnection表示已经建立或正在建立的连接。建立连接后,用户仅仅须要在上层类如TcpServer中设置连接到来和消息到来的处理函数,继而回调TcpConnection中的 setConnectionCallback和setMessageCallback函数,实现对事件的处理。

用户须要关心的事件是有限的。其它都由网络库负责。

TcpConnection中封装了InputBuffer和OutputBuffer,用来表示应用层的缓冲区。在发送数据时,假设不能一次将Buffer中的数据发送完成,它还会继续关注Channel中的可写事件,当sockfd可写时,会再次发送。

前面提到TcpConnection的生存期模糊,主要是由于我们不能在TcpServer中直接erase掉TcpConnection对象。由于此时有可能Channel中的handleEvent还在运行,假设析构TcpConnection对象。那么他的成员channel_也会被析构,会导致core
dump。也就是说我们须要TcpConnection 对象生存期要长于handleEvent() 函数,直到运行完connectDestroyed() 后才会析构。

断开连接: 

TcpConnection的断开是採用被动方式,即对方先关闭连接。本地read(2)返回0后。调用顺序例如以下: 

handleClose()->TcpServer::removeConnection->TcpConnection::connectDestroyed()。

详细我们查看以下的连接关闭时序图:

当连接到来,创建一个TcpConnection对象。立马用shared_ptr来管理,引用计数为1,在Channel中维护一个weak_ptr(tie_),将这个shared_ptr对象赋值给_tie。引用计数仍然为1。当连接关闭时,在handleEvent中,将tie_提升,得到一个shard_ptr对象,引用计数就变成了2。当shared_ptr的计数不为0时,TcpConnection不会被销毁。

TcpConnection.h

class TcpConnection : boost::noncopyable,
public boost::enable_shared_from_this<TcpConnection>
{
public:
/// Constructs a TcpConnection with a connected sockfd
///
/// User should not create this object.
TcpConnection(EventLoop* loop,
const string& name,
int sockfd,
const InetAddress& localAddr,
const InetAddress& peerAddr);
~TcpConnection(); EventLoop* getLoop() const { return loop_; }
const string& name() const { return name_; }
const InetAddress& localAddress() { return localAddr_; }
const InetAddress& peerAddress() { return peerAddr_; }
bool connected() const { return state_ == kConnected; } void setConnectionCallback(const ConnectionCallback& cb)
{ connectionCallback_ = cb; } void setMessageCallback(const MessageCallback& cb)
{ messageCallback_ = cb; } /// Internal use only.
void setCloseCallback(const CloseCallback& cb)
{ closeCallback_ = cb; } // called when TcpServer accepts a new connection
void connectEstablished(); // should be called only once
// called when TcpServer has removed me from its map
void connectDestroyed(); // should be called only once private:
enum StateE { kDisconnected, kConnecting, kConnected, kDisconnecting };
void handleRead(Timestamp receiveTime);
void handleClose();
void handleError();
void setState(StateE s) { state_ = s; } EventLoop* loop_; // 所属EventLoop
string name_; // 连接名
StateE state_; // FIXME: use atomic variable
// we don't expose those classes to client.
boost::scoped_ptr<Socket> socket_;
boost::scoped_ptr<Channel> channel_;
InetAddress localAddr_;
InetAddress peerAddr_;
ConnectionCallback connectionCallback_;
MessageCallback messageCallback_;
CloseCallback closeCallback_;
}; typedef boost::shared_ptr<TcpConnection> TcpConnectionPtr; }

TcpConnection.cc

TcpConnection::TcpConnection(EventLoop* loop,
const string& nameArg,
int sockfd,
const InetAddress& localAddr,
const InetAddress& peerAddr)
: loop_(CHECK_NOTNULL(loop)),
name_(nameArg),
state_(kConnecting),
socket_(new Socket(sockfd)),
channel_(new Channel(loop, sockfd)),
localAddr_(localAddr),
peerAddr_(peerAddr)/*,
highWaterMark_(64*1024*1024)*/
{
// 通道可读事件到来的时候。回调TcpConnection::handleRead。_1是事件发生时间
channel_->setReadCallback(
boost::bind(&TcpConnection::handleRead, this, _1));
// 连接关闭,回调TcpConnection::handleClose
channel_->setCloseCallback(
boost::bind(&TcpConnection::handleClose, this));
// 错误发生,回调TcpConnection::handleError
channel_->setErrorCallback(
boost::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();
} void TcpConnection::connectEstablished()
{
loop_->assertInLoopThread();
assert(state_ == kConnecting);
setState(kConnected);
LOG_TRACE << "[3] usecount=" << shared_from_this().use_count();
channel_->tie(shared_from_this());
channel_->enableReading(); // TcpConnection所相应的通道增加到Poller关注 connectionCallback_(shared_from_this());
LOG_TRACE << "[4] usecount=" << shared_from_this().use_count();
} void TcpConnection::connectDestroyed()
{
loop_->assertInLoopThread();
if (state_ == kConnected)
{
setState(kDisconnected);
channel_->disableAll(); connectionCallback_(shared_from_this());
}
channel_->remove();
} void TcpConnection::handleRead(Timestamp receiveTime)
{
/*
loop_->assertInLoopThread();
int savedErrno = 0;
ssize_t n = inputBuffer_.readFd(channel_->fd(), &savedErrno);
if (n > 0)
{
messageCallback_(shared_from_this(), &inputBuffer_, receiveTime);
}
else if (n == 0)
{
handleClose();
}
else
{
errno = savedErrno;
LOG_SYSERR << "TcpConnection::handleRead";
handleError();
}
*/
loop_->assertInLoopThread();
int savedErrno = 0;
char buf[65536];
ssize_t n = ::read(channel_->fd(), buf, sizeof buf);
if (n > 0)
{
messageCallback_(shared_from_this(), buf, n);
}
else if (n == 0)
{
handleClose();
}
else
{
errno = savedErrno;
LOG_SYSERR << "TcpConnection::handleRead";
handleError();
}

Channel中对tie_的处理:

void Channel::handleEvent(Timestamp receiveTime)
{
boost::shared_ptr<void> guard;
if (tied_)
{
guard = tie_.lock();
if (guard)
{
LOG_TRACE << "[6] usecount=" << guard.use_count();
handleEventWithGuard(receiveTime);
LOG_TRACE << "[12] usecount=" << guard.use_count();
}
}
else
{
handleEventWithGuard(receiveTime);
}
}

參考:

《linux多线程服务端编程》

《muduo使用手冊》

Muduo网络库源代码分析(六)TcpConnection 的生存期管理的更多相关文章

  1. Muduo网络库源代码分析(四)EventLoopThread和EventLoopThreadPool的封装

    muduo的并发模型为one loop per thread+ threadpool.为了方便使用,muduo封装了EventLoop和Thread为EventLoopThread,为了方便使用线程池 ...

  2. Muduo网络库实战(二):实现服务器与客户端的连接

    1. 方案的确定 1)基本需求 用户1000+, IO压力不大: 多个客户端打开网站,输入查询字符串strclient,发送给服务器=>服务器接收客户端发过来的数据并处理,将结果返回给客户端: ...

  3. 长文梳理muduo网络库核心代码、剖析优秀编程细节

    前言 muduo库是陈硕个人开发的tcp网络编程库,支持Reactor模型,推荐大家阅读陈硕写的<Linux多线程服务端编程:使用muduo C++网络库>.本人前段时间出于个人学习.找工 ...

  4. muduo网络库使用心得

    上个月看了朋友推荐的mudo网络库,下完代码得知是国内同行的开源作品,甚是敬佩.下了mudo使用手冊和035版的代码看了下结构,感觉是一个比較成熟并且方便使用的网络库.本人手头也有自己的网络库,尽管不 ...

  5. muduo网络库架构总结

    目录 muduo网络库简介 muduo网络库模块组成 Recator反应器 EventLoop的两个组件 TimerQueue定时器 Eventfd Connector和Acceptor连接器和监听器 ...

  6. muduo 网络库学习之路(一)

    前提介绍: 本人是一名大三学生,主要使用C++开发,兴趣是高性能的服务器方面. 网络开发离不开网络库,所以今天开始学一个新的网络库,陈老师的muduo库 我参考的书籍就是陈老师自己关于muduo而编著 ...

  7. 陈硕 - Linux 多线程服务端编程 - muduo 网络库作者

    http://chenshuo.com/book/ Muduo网络库源码分析(一) EventLoop事件循环(Poller和Channel)http://blog.csdn.net/nk_test/ ...

  8. muduo网络库学习笔记(五) 链接器Connector与监听器Acceptor

    目录 muduo网络库学习笔记(五) 链接器Connector与监听器Acceptor Connector 系统函数connect 处理非阻塞connect的步骤: Connetor时序图 Accep ...

  9. muduo网络库学习笔记(四) 通过eventfd实现的事件通知机制

    目录 muduo网络库学习笔记(四) 通过eventfd实现的事件通知机制 eventfd的使用 eventfd系统函数 使用示例 EventLoop对eventfd的封装 工作时序 runInLoo ...

随机推荐

  1. [Java基础] Java多线程-工具篇-BlockingQueue

    转载自: http://www.cnblogs.com/jackyuj/archive/2010/11/24/1886553.html 前言: 在新增的Concurrent包中,BlockingQue ...

  2. Heartbleed漏洞利用程序

    #!/usr/bin/python # Quick and dirty demonstration of CVE-2014-0160 by Jared Stafford (jspenguin@jspe ...

  3. 一起來玩鳥 Starling Framework(3)Button!

    週末夜來介紹個簡單的DisplayObject就好.不論是在電腦上或行動裝置上,跟使用者互動次數最多的,大概就是按鈕了.因此,Starling有個Button類別,來做出這個常用的互動元件. 同樣是因 ...

  4. ubuntu14.04 server 安装docker

    安装docker服务 $ curl -sSL https://get.docker.com/ | sh   $ ocker run hello-world 测试docker是否安装成功 ubuntu ...

  5. Node.js nvshens图片批量下载爬虫 1.00

    //====================================================== // www.nvshens.com图片批量下载Node.js爬虫1.00 // 此程 ...

  6. Quartz定时框架入门

    Quartz框架是Java开源的定时任务调度器,Quartz框架中有如下核心概念: 1. Job 任务接口,接口中只声明方法void execute(JobExecutionContext conte ...

  7. Problem-1001:Sum Problem

    Sum Problem Sample code : #include <stdio.h> int main() { int i,n; int sum; while(scanf(" ...

  8. hibernate(jpa)中注解配置字段为主键

    http://www.blogjava.net/ITdavid/archive/2009/02/25/256605.html 注解方式的主键配置     非自增字段为主键,注解annotation表示 ...

  9. css解决方案之css布局

    固定宽度布局解决方案 固定宽度的居中布局 首先我们这里探讨的是我们常用的固定宽度的居中布局,这应该是目前实战中应用的最多的了. 然后从考虑是否应用大背景上我们分为两种,一种是允许整体有一个大背景,一种 ...

  10. Linux----LVM扩容磁盘空间(讲的也很好)

    转:https://www.cnblogs.com/tail-f/p/6143085.html