东阳的学习笔记
muduo 尽量让依赖是单向的,比如,TcpServer 会用到 Acceptor,但 Acceptor 并不知道 TcpServer 的存在。
本文会介绍 TcpServer 并初步实现 TcpConnection,本文只处理连接的建立,下一篇博客会处理连接的断开。
TcpServer 新建连接的相关函数调用顺序见图 8-4 (有的函数名简写,省略了 poll(2) 调用)。
- 其中 Channel::handleEvent() 的触发条件是
listening socket
可读,表明有新连接到达。 - TcpServer 会为新连接创建对应的
TcpConnection
对象。
一、TcpServer class
TcpServer class 的功能是管理 accept(2) 获得的 TcpConnection。TcpServer 是供用户直接使用的,生命期由用户控制。TcpServer 的接口如下,用户只需要设置好 callback,再调用 start() 即可
- TcpServer 内部使用 Acceptor 来获得新连接的 fd。
- TcpServer 保存用户提供的
ConnectionCallback
和MessageCallback
,在新建TcpConnection
时, 传给其构造函数。- TcpServer 持有目前存活的 TcpConnection 的 shared_ptr (即
TcpConnectionPtr
),因为 TcpConnection对象的生命期是模糊的
,用户也可以持有TcpCOnnectionPtr
。class TcpServer : boost::noncopyable { public: TcpServer(EventLoop* loop, const InetAddress& listenAddr); ~TcpServer(); // force out-line dtor, for scoped_ptr members. /// Starts the server if it's not listenning. /// /// 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; }
1.1 TcpSever 的数据成员
每个 TcpConnect 对象有一个名字,这个名字是由其所属的 TcpServer 在创建 TcpConnection 对象时生成,
名字是 ConnectionMap 的 key.
private: /// Not thread safe, but in loop void newConnection(int sockfd, const InetAddress& peerAddr); typedef std::map<std::string, TcpConnectionPtr> ConnectionMap; EventLoop* loop_; // the acceptor loop const std::string name_; // 名字是 ConnectionMap 的 key. boost::scoped_ptr<Acceptor> acceptor_; // avoid revealing Acceptor ConnectionCallback connectionCallback_; MessageCallback messageCallback_; bool started_; int nextConnId_; // always in loop thread ConnectionMap connections_; };
1.2 TcpServer::newConnection()
新连接到达时,Acceptor 会回调 newConnection(),
后者会创建 TcpConnection 对象 conn
- 创建好 conn, 并将其放入
ConnectionMap
,设置好 callback。- 再调用 conn->connectEstablished(),其中会回调用户提供的 ConnectionCallback。
&16 可以使用 make_share 节约一次 new(为啥?make_share 代替 new)
见通过new和make_shared构造shared_ptr的性能差异void TcpServer::newConnection(int sockfd, const InetAddress& peerAddr) { loop_->assertInLoopThread(); char buf[32]; snprintf(buf, sizeof buf, "#%d", nextConnId_); ++nextConnId_; std::string connName = name_ + buf; LOG_INFO << "TcpServer::newConnection [" << name_ << "] - new connection [" << connName << "] from " << peerAddr.toHostPort(); InetAddress localAddr(sockets::getLocalAddr(sockfd)); // FIXME poll with zero timeout to double confirm the new connection TcpConnectionPtr conn( new TcpConnection(loop_, connName, sockfd, localAddr, peerAddr)); connections_[connName] = conn; conn->setConnectionCallback(connectionCallback_); conn->setMessageCallback(messageCallback_); conn->connectEstablished(); }
二、TcpConnection class
TcpConnection class 可谓是 muduo
最核心也是最复杂
的 class(源文件和头文件一共有450多行,是 muduo 中最大的 class)
TcpConnection 是 muduo 里唯一使用 shared_ptr 来管理的 class,也是唯一继承enable_shared_from_this
的class,这源于其模糊的生命期。
- TcpConnection 表示的是
一次TCP连接
,它是不可再生的,一但连接断开,这个 TcpConnection 对象就没啥用了。- TcpConnection 没有发起连接的功能,其构造函数的参数是已经建立连接的 socket fd (无论是
TcpServer
被动接受还是 TcpClient 主动发起),因此其初始状态是kConnection
这里的 TcpConnection 没有可供用户使用的接口。主动发起
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 std::string& name, int sockfd, const InetAddress& localAddr, const InetAddress& peerAddr); ~TcpConnection(); EventLoop* getLoop() const { return loop_; } const std::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. // called when TcpServer accepts a new connection void connectEstablished(); // should be called only once private: enum StateE { kConnecting, kConnected, }; // 在s05中只有这两个,后面还会扩充 void setState(StateE s) { state_ = s; } void handleRead(); EventLoop* loop_; std::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_; };
2.1 MessageCallback()
这个版本MessageCallback 定义很原始(简陋):
- 没有使用Buffer class,而只是把(const char * buf, int len) 传给用户,这种接口用起来无疑是很不方便的。
void TcpConnection::handleRead() { char buf[65536]; ssize_t n = ::read(channel_->fd(), buf, sizeof buf); messageCallback_(shared_from_this(), buf, n); // FIXME: close connection if n == 0 // s05中的版本没有处理断开连接 }