東陽的學習筆記
muduo 盡量讓依賴是單向的,比如,TcpServer 會用到 Acceptor,但 Acceptor 并不知道 TcpServer 的存在。
本文會介紹 TcpServer 并初步實作 TcpConnection,本文隻處理連接配接的建立,下一篇部落格會處理連接配接的斷開。
TcpServer 建立連接配接的相關函數調用順序見圖 8-4 (有的函數名簡寫,省略了 poll(2) 調用)。
- 其中 Channel::handleEvent() 的觸發條件是
可讀,表明有新連接配接到達。listening socket
- TcpServer 會為新連接配接建立對應的
對象。TcpConnection
muduo庫學習之設計與實作06——TcpServer 接受新連接配接
一、TcpServer class
TcpServer class 的功能是管理 accept(2) 獲得的 TcpConnection。TcpServer 是供使用者直接使用的,生命期由使用者控制。TcpServer 的接口如下,使用者隻需要設定好 callback,再調用 start() 即可
- TcpServer 内部使用 Acceptor 來獲得新連接配接的 fd。
- TcpServer 儲存使用者提供的
和
ConnectionCallback
,在建立
MessageCallback
時, 傳給其構造函數。
TcpConnection
- TcpServer 持有目前存活的 TcpConnection 的 shared_ptr (即
),因為 TcpConnection
TcpConnectionPtr
,使用者也可以持有
對象的生命期是模糊的
。
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, 并将其放入
,設定好 callback。
ConnectionMap
- 再調用 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,也是唯一繼承
的class,這源于其模糊的生命期。
enable_shared_from_this
這裡的 TcpConnection 沒有可供使用者使用的接口。主動發起
- TcpConnection 表示的是
,它是不可再生的,一但連接配接斷開,這個 TcpConnection 對象就沒啥用了。
一次TCP連接配接
- TcpConnection 沒有發起連接配接的功能,其構造函數的參數是已經建立連接配接的 socket fd (無論是
被動接受還是 TcpClient 主動發起),是以其初始狀态是
TcpServer
kConnection
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中的版本沒有處理斷開連接配接 }