muduo库学习之设计与实现06——TcpServer 接受新连接

东阳的学习笔记

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 保存用户提供的 ConnectionCallbackMessageCallback,在新建 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(),

  1. 后者会创建 TcpConnection 对象 conn
  2. 创建好 conn, 并将其放入 ConnectionMap,设置好 callback。
  3. 再调用 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中的版本没有处理断开连接
}

猜你喜欢

转载自blog.csdn.net/qq_22473333/article/details/113722296