enable_shared_from_this
struct TcpConn: public std::enable_shared_from_this<TcpConn>, private noncopyable
TcpConn继承了std::enable_shared_from_this<TcpConn>
,使得TcpConn可以安全的向外传递自己的智能指针,而不是传出一个危险的裸指针。
可以参考这里学习:
http://www.cplusplus.com/reference/memory/enable_shared_from_this/?kw=enable_shared_from_this
成员变量
public:
EventBase* base_; // 事件循环
Channel* channel_; // 已连接套接字对应的Channel
Buffer input_, output_; // 输入/输出缓冲区
Ip4Addr local_, peer_;
State state_; // 状态
TcpCallBack readcb_, writablecb_, statecb_; // 一些回调函数
std::list<IdleId> idleIds_;
TimerId timeoutId_;
AutoContext ctx_, internalCtx_;
std::string destHost_, localIp_;
int destPort_, connectTimeout_, reconnectInterval_;
int64_t connectedTime_;
std::unique_ptr<CodecBase> codec_;
TcpConn表示一个Tcp连接。channel_
表示已连接套接字对应的Channel。
muduo对于非阻塞网络编程为什么需要输入/输出缓冲区做了很详细的解释。(chapter 6.4 P137有讨论)。handy作为一个非阻塞的网络库自然有输入/输出缓冲区。
此外,handy使用状态机描述一个连接的生命期:
//Tcp连接的个状态
enum State { Invalid=1, Handshaking, Connected, Closed, Failed, };
构造函数/析构函数
TcpConn::TcpConn()
:base_(NULL),
channel_(NULL),
state_(State::Invalid), // 初始状态为 Invalid
destPort_(-1),
connectTimeout_(0),
reconnectInterval_(-1),
connectedTime_(util::timeMilli())
{
}
TcpConn::~TcpConn() {
delete channel_; // 析构channel
}
TcpConn::attach
上一篇的最后提到了TcpServer对TcpConn的使用。TcpConn被创建后,调用的第一个函数就是attach。
void TcpConn::attach(EventBase* base, int fd, Ip4Addr local, Ip4Addr peer)
{
fatalif((destPort_<=0 && state_ != State::Invalid) || (destPort_>=0 && state_ != State::Handshaking),
"you should use a new TcpConn to attach. state: %d", state_);
base_ = base;
state_ = State::Handshaking;
local_ = local;
peer_ = peer;
if (channel_) { delete channel_; }
// 创建一个Channel,向Poller注册已连接套接字的可读和可写事件
channel_ = new Channel(base, fd, kWriteEvent|kReadEvent);
trace("tcp constructed %s - %s fd: %d",
local_.toString().c_str(),
peer_.toString().c_str(),
fd);
// 获得自己的shared_ptr
TcpConnPtr con = shared_from_this();
// 设置读写回调函数
con->channel_->onRead([=] { con->handleRead(con); });
con->channel_->onWrite([=] { con->handleWrite(con); });
}
attach函数的作用就是,创建一个关于已连接套接字的Channel,并设置好读写回调函数。可以说TcpConn也是Channel和EventBase的使用者。
思考一些问题
从accept得到的socket这里称为已连接套接字,它们是在哪里被close的呢?
答案:是在Channel的析构函数被调用时。当TcpConn被销毁时,Channel会被delete,socket就在析构函数执行是被关闭了。虽然socket的创建不是在Channel中,但是关闭却需要Channel来执行,显得有些不对称。muduo中的socket是一个RAII,可能更好吧。