木铎源码剖析—TcpConnection

TCPConnection

​ 在上面将Acceptor的时候提到了这个TcpConnection类,这个类主要封装了一个已建立的TCP连接,以及控制该TCP连接的方法(连接建立和关闭和销毁) ,以及该连接发生的各种事件(读/写/错误/连接)对应的处理函数,以及这个TCP连接的服务器和客户端的套接字地址信息等。

TcpConnection类和Acceptor类是兄弟关系,Acceptor用于main EventLoop中,对服务器监听套接字fd及其相关方法进行封装(监听、接受连接、分发连接给SubEventLoop),TcpConnection用于SubEventLoop中,对连接套接字fd及其相关方法进行封装(读消息事件、发送消息事件、连接关闭事件、错误事件等);

TcpConnection的成员变量

EventLoop* loop_;//Tcp连接的Channel注册到了哪一个subEventLoop上,这个loop_就是那一个subEventLoop
const string name_;//服务器名称
StateE state_;  // FIXME: use atomic variable
bool reading_; //是否正在读取数据
// we don't expose those classes to client.
std::unique_ptr<Socket> socket_;//用于保持已连接套接字文件描述符
std::unique_ptr<Channel> channel_;//用于注册上面的socket_及其各类事件的处理函数
const InetAddress localAddr_;//本地地址
const InetAddress peerAddr_;//对方地址
ConnectionCallback connectionCallback_;//连接建立时的回调函数
MessageCallback messageCallback_;//消息到达时的回调函数
WriteCompleteCallback writeCompleteCallback_;//消息写入完成时的回调函数
HighWaterMarkCallback highWaterMarkCallback_;//高水位标志回调函数
CloseCallback closeCallback_;//连接关闭时的回调函数
size_t highWaterMark_;//高水位标志
Buffer inputBuffer_;//是该Tcp连接对应的用户接受缓冲区
Buffer outputBuffer_; // FIXME: use list<Buffer> as output buffer 用于暂存那些暂时发送不出去的待发送数据 
//因为Tcp发送缓冲区是有大小限制的,假如达到了高水位线,
//就没办法把发送的数据通过send()直接拷贝到Tcp发送缓冲区,
//而是暂存在这个outputBuffer_中,等TCP发送缓冲区有空间了,
//触发可写事件了,再把outputBuffer_中的数据拷贝到Tcp发送缓冲区中。
boost::any context_;//用户自定义的上下文数据

数据设置及访问方法

bool isReading() const { return reading_; };
void setContext(const boost::any& context)  { context_ = context; }
const boost::any& getContext() const  { return context_; }
boost::any* getMutableContext()  { return &context_; }
void setConnectionCallback(const ConnectionCallback& cb) { connectionCallback_ = cb; }
void setMessageCallback(const MessageCallback& cb){ messageCallback_ = cb; }
void setWriteCompleteCallback(const WriteCompleteCallback& cb){ writeCompleteCallback_ = cb; }
void setHighWaterMarkCallback(const HighWaterMarkCallback& cb, size_t highWaterMark){ highWaterMarkCallback_ = cb; highWaterMark_ = highWaterMark; }
Buffer* inputBuffer() { return &inputBuffer_; }
Buffer* outputBuffer()  { return &outputBuffer_; }
void setCloseCallback(const CloseCallback& cb)  { closeCallback_ = cb; }
EventLoop* getLoop() const { return loop_; }
const string& name() const { return name_; }
const InetAddress& localAddress() const { return localAddr_; }
const InetAddress& peerAddress() const { return peerAddr_; }
bool connected() const { return state_ == kConnected; }
bool disconnected() const { return state_ == kDisconnected; }
bool getTcpInfo(struct tcp_info*) const;
string getTcpInfoString() const;

构造函数

TcpConnection::TcpConnection(EventLoop* loop,
                             const string& nameArg,
                             int sockfd,
                             const InetAddress& localAddr,
                             const InetAddress& peerAddr)
  : loop_(CHECK_NOTNULL(loop)),
    name_(nameArg),
    state_(kConnecting),
    reading_(true),
    socket_(new Socket(sockfd)),
    channel_(new Channel(loop, sockfd)),
    localAddr_(localAddr),
    peerAddr_(peerAddr),
    highWaterMark_(64*1024*1024)
{
  channel_->setReadCallback(
      std::bind(&TcpConnection::handleRead, this, _1));
  channel_->setWriteCallback(
      std::bind(&TcpConnection::handleWrite, this));
  channel_->setCloseCallback(
      std::bind(&TcpConnection::handleClose, this));
  channel_->setErrorCallback(
      std::bind(&TcpConnection::handleError, this));
  LOG_DEBUG << "TcpConnection::ctor[" <<  name_ << "] at " << this
            << " fd=" << sockfd;
  socket_->setKeepAlive(true);
}

void handleRead(Timestamp receiveTime);:可读事件处理,

主要处理发生的读事件,通过buffer中封装好的readFD函数进行数据获取,获取结果分为三种:

  1. 大于0:表示读取到了发送过来的数据,此时需要调用消息回调函数,将此数据进行传递
  2. 等于0:表示客户端已经关闭连接,直接调用关闭处理函数
  3. 小于0:数据传输发生错误,调用错误处理函数
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();
  }
}

void TcpConnection::handleClose():关闭事件处理

首先关闭通道的所有处理事件,然后调用connectionCallback_回调函数,执行关闭后的处理,

void TcpConnection::handleClose()
{
  loop_->assertInLoopThread();
  LOG_TRACE << "fd = " << channel_->fd() << " state = " << stateToString();
  assert(state_ == kConnected || state_ == kDisconnecting);
  // we don't close fd, leave it to dtor, so we can find leaks easily.
  setState(kDisconnected);
  channel_->disableAll();

  TcpConnectionPtr guardThis(shared_from_this());
  //这两个回调函数分别执行连接建立和关闭后的回调、关闭后的回调函数
  connectionCallback_(guardThis);
  // must be the last line
  closeCallback_(guardThis);
}

void TcpConnection::handleError():处理错误

void TcpConnection::handleError()
{
  int err = sockets::getSocketError(channel_->fd());
  LOG_ERROR << "TcpConnection::handleError [" << name_
            << "] - SO_ERROR = " << err << " " << strerror_tl(err);
}

void TcpConnection::handleWrite():可写事件处理

该事件主要是将需要传输的ouputBuffer_中的数据通过write进行传输,写完之后,返回值大于0则是正常传输的,否则发生错误,传输之后需要后置可读游标,并且执行读完之后的回调函数,以及shutdown写事件

void TcpConnection::handleWrite()
{
  loop_->assertInLoopThread();
  if (channel_->isWriting())
  {
    ssize_t n = sockets::write(channel_->fd(),
                               outputBuffer_.peek(),
                               outputBuffer_.readableBytes());
    if (n > 0)
    {
      outputBuffer_.retrieve(n);
      if (outputBuffer_.readableBytes() == 0)
      {
        channel_->disableWriting();
        if (writeCompleteCallback_)
        {
          loop_->queueInLoop(std::bind(writeCompleteCallback_, shared_from_this()));
        }
        if (state_ == kDisconnecting)
        {
          shutdownInLoop();
        }
      }
    }
    else
    {
      LOG_SYSERR << "TcpConnection::handleWrite";
      // if (state_ == kDisconnecting)
      // {
      //   shutdownInLoop();
      // }
    }
  }
  else
  {
    LOG_TRACE << "Connection fd = " << channel_->fd()
              << " is down, no more writing";
  }
}

此类给出了默认的connectionCallback_以及messageCallback_回调函数

void muduo::net::defaultConnectionCallback(const TcpConnectionPtr& conn)
{
  LOG_TRACE << conn->localAddress().toIpPort() << " -> "
            << conn->peerAddress().toIpPort() << " is "
            << (conn->connected() ? "UP" : "DOWN");
  // do not call conn->forceClose(), because some users want to register message callback only.
}

void muduo::net::defaultMessageCallback(const TcpConnectionPtr&,
                                        Buffer* buf,
                                        Timestamp)
{
  buf->retrieveAll();
}

void TcpConnection::send(const StringPiece& message):发送数据

该函数会判断是否在EventInloop中发送数据,关于是否的问题,我们发现其实如果不是那么程序会建立一个成员函数指针,然后通过EventLoop中的runinloop函数执行该函数,所以最终的发送其实主题函数是下面这个:

void TcpConnection::sendInLoop(const void* data, size_t len) 这个函数很复杂 需要仔细阅读

首先会设置两个标志分别用于记录已经发送的,以及剩下的及nwrote+remaining=len永远是成立的

如果该文件描述符没有在写且outputBuffer为空这直接写入,这时候会出现剩余,

然后如何现在有剩余且outputBuffer中也有值,那么需要判断大小来确定是否调用highWaterMarkCallback_回调函数,

最后将所有数据写入outputBuffer中,且触发写事件,执行handleWrite

void TcpConnection::sendInLoop(const void* data, size_t len)
{
  loop_->assertInLoopThread();
  ssize_t nwrote = 0;
  size_t remaining = len;
  bool faultError = false;
  if (state_ == kDisconnected)
  {
    LOG_WARN << "disconnected, give up writing";
    return;
  }
  // if no thing in output queue, try writing directly
  if (!channel_->isWriting() && outputBuffer_.readableBytes() == 0)
  {
    nwrote = sockets::write(channel_->fd(), data, len);
    if (nwrote >= 0)
    {
      remaining = len - nwrote;
      if (remaining == 0 && writeCompleteCallback_)
      {
        loop_->queueInLoop(std::bind(writeCompleteCallback_, shared_from_this()));
      }
    }
    else // nwrote < 0
    {
      nwrote = 0;
      if (errno != EWOULDBLOCK)
      {
        LOG_SYSERR << "TcpConnection::sendInLoop";
        if (errno == EPIPE || errno == ECONNRESET) // FIXME: any others?
        {
          faultError = true;
        }
      }
    }
  }

  assert(remaining <= len);
  if (!faultError && remaining > 0)
  {
    size_t oldLen = outputBuffer_.readableBytes();
    if (oldLen + remaining >= highWaterMark_
        && oldLen < highWaterMark_
        && highWaterMarkCallback_)
    {
      loop_->queueInLoop(std::bind(highWaterMarkCallback_, shared_from_this(), oldLen + remaining));
    }
    outputBuffer_.append(static_cast<const char*>(data)+nwrote, remaining);
    if (!channel_->isWriting())
    {
      channel_->enableWriting();
    }
  }
}

其余的实现都是基于上面这个send的

void TcpConnection::send(const void* data, int len)
{
  send(StringPiece(static_cast<const char*>(data), len));
}

void TcpConnection::send(const StringPiece& message)
{
  if (state_ == kConnected)
  {
    if (loop_->isInLoopThread())
    {
      sendInLoop(message);
    }
    else
    {
      void (TcpConnection::*fp)(const StringPiece& message) = &TcpConnection::sendInLoop;
      loop_->runInLoop(
          std::bind(fp,
                    this,     // FIXME
                    message.as_string()));
                    //std::forward<string>(message)));
    }
  }
}

// FIXME efficiency!!!
void TcpConnection::send(Buffer* buf)
{
  if (state_ == kConnected)
  {
    if (loop_->isInLoopThread())
    {
      sendInLoop(buf->peek(), buf->readableBytes());
      buf->retrieveAll();
    }
    else
    {
      void (TcpConnection::*fp)(const StringPiece& message) = &TcpConnection::sendInLoop;
      loop_->runInLoop(
          std::bind(fp,
                    this,     // FIXME
                    buf->retrieveAllAsString()));
                    //std::forward<string>(message)));
    }
  }
}

void TcpConnection::sendInLoop(const StringPiece& message)
{
  sendInLoop(message.data(), message.size());
}

void TcpConnection::connectEstablished():连接建立后的处理方式

void TcpConnection::connectEstablished()
{
  loop_->assertInLoopThread();
  assert(state_ == kConnecting);
  setState(kConnected);
  channel_->tie(shared_from_this());
  channel_->enableReading();

  connectionCallback_(shared_from_this());
}

void TcpConnection::connectDestroyed()连接断开后的处理方式

void TcpConnection::connectDestroyed()
{
  loop_->assertInLoopThread();
  if (state_ == kConnected)
  {
    setState(kDisconnected);
    channel_->disableAll();

    connectionCallback_(shared_from_this());
  }
  channel_->remove();
}

​ 总结:TcpConnection其实是一个针对以及建立TCP连接后对该套接字进行的一系列方法封装,正如我们认为的一样,建立连接后肯定会进行一系列的数据发送接受等事件,这个类就是把这些事件分别进行监听,通过调用回调函数分别处理,同时针对发送和接受的数据,也通过性能非常好的Buffer进行缓冲;对于数据的发送并不是直接交给写事件处理的,而是先发送一些,如果太大的话,才会唤醒写事件进行发送;对于读事件的开始或者停止也封装了对应的方法,可以说是一个加强版的Channel保姆,Channel是专门监听某一个文件描述符发生的事情,而这个TcpConnection则是专门针对sock文件描述符进行监听。

posted @ 2024-03-01 11:23  孟秋十三  阅读(26)  评论(0)    收藏  举报