【muduo】net篇---TcpConnection

  TcpConnection类主要负责封装一次TCP连接,向Channel类注册回调函数(可读、可写、可关闭、错误处理),将来当Channel类上的事件发生时,调用相应的回调函数进行数据收发或者错误处理。

  TcpConnection是使用shared_ptr来管理的类,因为它的生命周期模糊。TcpConnection表示已经建立或正在建立的连接,建立连接后,用户只需要在上层类如TcpServer中设置连接到来和消息到来的处理函数,继而回调TcpConnection中的 setConnectionCallback和setMessageCallback函数,实现对事件的处理。用户需要关心的事件是有限的,其他都由网络库负责。

  TcpConnection中封装了InputBuffer和OutputBuffer,用来表示应用层的缓冲区。在发送数据时,如果不能一次将Buffer中的数据发送完毕,它还会继续关注Channel中的可写事件,当sockfd可写时,会再次发送。

#include <muduo/net/TcpConnection.h>
#include <muduo/base/Logging.h>
#include <muduo/base/WeakCallback.h>
#include <muduo/net/Channel.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/Socket.h>
#include <muduo/net/SocketsOps.h>

#include <errno.h>

using namespace muduo;
using namespace muduo::net;

void muduo::net::defaultConnectionCallback(const TcpConnectionPtr& conn)
{
  LOG_TRACE << conn->localAddress().toIpPort() << " -> "
            << conn->peerAddress().toIpPort() << " is "
            << (conn->connected() ? "UP" : "DOWN");
}

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

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对象注册可读事件
  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);
}

TcpConnection::~TcpConnection()
{
  LOG_DEBUG << "TcpConnection::dtor[" <<  name_ << "] at " << this
            << " fd=" << channel_->fd()
            << " state=" << stateToString();
  assert(state_ == kDisconnected);
}

bool TcpConnection::getTcpInfo(struct tcp_info* tcpi) const
{
  return socket_->getTcpInfo(tcpi);
}

string TcpConnection::getTcpInfoString() const
{
  char buf[1024];
  buf[0] = '\0';
  socket_->getTcpInfoString(buf, sizeof buf);
  return buf;
}

/******************************************************************** 
Modify : Eric Lee
Date : 2018-01-20
Description : 发数据是主动的,用户一般可用TcpConnection::send() 
来发送数据,最终会调到TcpConnection::sendInLoop()。如果数据不能一次
发完,则打开channel的写事件开关,分开几次发。
*********************************************************************/
void TcpConnection::send(const void* data, int len)
{
  send(StringPiece(static_cast<const char*>(data), len));
}

// 实际调用的是sendInLoop,在IO线程中去调用,保证安全性
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, message.as_string()));
    }
  }
}

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, buf->retrieveAllAsString()));
    }
  }
}

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

/******************************************************************** 
Modify : Eric Lee
Date : 2018-01-20
Description : 实际的发送数据函数。
*********************************************************************/
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;
  }
  // 如果当前channel没有写事件发生,或者发送buffer已经清空,那么就不通过缓冲区直接发送数据
  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;
      if (errno != EWOULDBLOCK)
      {
        LOG_SYSERR << "TcpConnection::sendInLoop";
        if (errno == EPIPE || errno == ECONNRESET)
        {
          faultError = true;
        }
      }
    }
  }
  // 如果还有残留的数据没有发送完成
  assert(remaining <= len);
  if (!faultError && remaining > 0)
  {
    size_t oldLen = outputBuffer_.readableBytes();
    // 如果输出缓冲区的数据已经超过高水位标记,那么调用highWaterMarkCallback_
    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);
    // 监听channel的可写事件(因为还有数据未发完),
    // 当可写事件被触发,就可以继续发送了,调用的是TcpConnection::handleWrite()
    if (!channel_->isWriting())
    {
      channel_->enableWriting();
    }
  }
}

void TcpConnection::shutdown()
{
  if (state_ == kConnected)
  {
    setState(kDisconnecting);
    loop_->runInLoop(std::bind(&TcpConnection::shutdownInLoop, this));
  }
}

/******************************************************************** 
Modify : Eric Lee
Date : 2018-01-20
Description : 主动关闭,调用TcpConnection::shutdown()。
*********************************************************************/
void TcpConnection::shutdownInLoop()
{
  loop_->assertInLoopThread();
  // 先判断是否发送buffer里还有东西,若有,暂时不关闭
  if (!channel_->isWriting())
  {
    // 关闭写功能
    socket_->shutdownWrite();
  }
}

void TcpConnection::forceClose()
{
  if (state_ == kConnected || state_ == kDisconnecting)
  {
    setState(kDisconnecting);
    loop_->queueInLoop(std::bind(&TcpConnection::forceCloseInLoop, shared_from_this()));
  }
}

void TcpConnection::forceCloseWithDelay(double seconds)
{
  if (state_ == kConnected || state_ == kDisconnecting)
  {
    setState(kDisconnecting);
    loop_->runAfter(seconds, makeWeakCallback(shared_from_this(), &TcpConnection::forceClose));
  }
}

void TcpConnection::forceCloseInLoop()
{
  loop_->assertInLoopThread();
  if (state_ == kConnected || state_ == kDisconnecting)
  {
    handleClose();
  }
}

const char* TcpConnection::stateToString() const
{
  switch (state_)
  {
    case kDisconnected:
      return "kDisconnected";
    case kConnecting:
      return "kConnecting";
    case kConnected:
      return "kConnected";
    case kDisconnecting:
      return "kDisconnecting";
    default:
      return "unknown state";
  }
}

void TcpConnection::setTcpNoDelay(bool on)
{
  socket_->setTcpNoDelay(on);
}

void TcpConnection::startRead()
{
  loop_->runInLoop(std::bind(&TcpConnection::startReadInLoop, this));
}

void TcpConnection::startReadInLoop()
{
  loop_->assertInLoopThread();
  if (!reading_ || !channel_->isReading())
  {
    channel_->enableReading();
    reading_ = true;
  }
}

void TcpConnection::stopRead()
{
  loop_->runInLoop(std::bind(&TcpConnection::stopReadInLoop, this));
}

void TcpConnection::stopReadInLoop()
{
  loop_->assertInLoopThread();
  if (reading_ || channel_->isReading())
  {
    channel_->disableReading();
    reading_ = false;
  }
}

/******************************************************************** 
Modify : Eric Lee
Date : 2018-01-20
Description : TcpConnection建立连接完成。
*********************************************************************/
void TcpConnection::connectEstablished()
{
  loop_->assertInLoopThread();
  assert(state_ == kConnecting); // 正处于连接建立过程
  setState(kConnected);
  channel_->tie(shared_from_this());
  // 每个连接对应一个channel,打开描述符的可读属性
  channel_->enableReading();
  // 连接成功,回调客户注册的函数(由用户提供的函数,比如OnConnection)
  connectionCallback_(shared_from_this());
}

/******************************************************************** 
Modify : Eric Lee
Date : 2018-01-20
Description : TcpConnection::connectDestroyed()是TcpConnection析构前
最后调用的一个函数,它通知用户连接已断开。
*********************************************************************/
void TcpConnection::connectDestroyed()
{
  loop_->assertInLoopThread();
  if (state_ == kConnected)
  {
    setState(kDisconnected);
    channel_->disableAll();
    // 仅仅起到记录作用而已
    connectionCallback_(shared_from_this());
  }
  // 在poller中移除channel
  channel_->remove();
}

/******************************************************************** 
Modify : Eric Lee
Date : 2018-01-20
Description : 
当某个channel有读事件发生时,会调用TcpConnection::handleRead()函数,
然后从socket里读入数据到buffer,再通过回调把这些数据返回给用户层。
*********************************************************************/
void TcpConnection::handleRead(Timestamp receiveTime)
{
  loop_->assertInLoopThread(); // 断言是否在loop线程
  int savedErrno = 0; 
  // 读数据到inputBuffer_中
  ssize_t n = inputBuffer_.readFd(channel_->fd(), &savedErrno);
  if (n > 0)
  {
    // 用户提供的处理信息的回调函数(由用户自己提供的函数,比如OnMessage)
    messageCallback_(shared_from_this(), &inputBuffer_, receiveTime);
  }
  // 读到了0,表明客户端已经关闭了
  else if (n == 0)
  {
    handleClose();
  }
  else
  {
    errno = savedErrno;
    LOG_SYSERR << "TcpConnection::handleRead";
    handleError();
  }
}

/******************************************************************** 
Modify : Eric Lee
Date : 2018-01-20
Description : 当可写事件触发时,调用TcpConnection::handleWrite()。
*********************************************************************/
void TcpConnection::handleWrite()
{
  loop_->assertInLoopThread();
  if (channel_->isWriting())
  {
    // 写数据
    ssize_t n = sockets::write(channel_->fd(), outputBuffer_.peek(), outputBuffer_.readableBytes());
    if (n > 0)
    {
      // 调整发送buffer的内部index,以便下次继续发送
      outputBuffer_.retrieve(n);
      // 如果可读的数据量为0,这里的可读是针对系统发送函数来说的,不是针对用户
      // 如果对于系统发送函数来说,可读的数据量为0,表示所有数据都被发送完毕了,即写完成了
      if (outputBuffer_.readableBytes() == 0)
      {
        // 不再关注写事件
        channel_->disableWriting();
        if (writeCompleteCallback_)
        {
          // 调用用户的写完成回调函数
          loop_->queueInLoop(std::bind(writeCompleteCallback_, shared_from_this()));
        }
        // 如果当前状态是正在关闭连接,那么就调用shutdown来主动关闭连接
        if (state_ == kDisconnecting)
        {
          shutdownInLoop();
        }
      }
    }
    else
    {
      LOG_SYSERR << "TcpConnection::handleWrite";
    }
  }
  else
  {
    LOG_TRACE << "Connection fd = " << channel_->fd() << " is down, no more writing";
  }
}

/******************************************************************** 
Modify : Eric Lee
Date : 2018-01-20
Description : 当对端调用shutdown()关闭连接时,本端会收到一个FIN,
channel的读事件被触发,但inputBuffer_.readFd() 会返回0,然后调用
handleClose(),处理关闭事件,最后调用TcpServer::removeConnection()。
*********************************************************************/
void TcpConnection::handleClose()
{
  // 断言是否在loop线程
  loop_->assertInLoopThread();
  LOG_TRACE << "fd = " << channel_->fd() << " state = " << stateToString();
  assert(state_ == kConnected || state_ == kDisconnecting);
  setState(kDisconnected);
  // channel上不再关注任何事情
  channel_->disableAll();
  // 获得shared_ptr交由tcpsever处理
  TcpConnectionPtr guardThis(shared_from_this());
  // 作用是记录一些日志
  connectionCallback_(guardThis);
  // 回调TcpServer::removeConnection(),TcpConnection的生命期由TcpServer控制
  closeCallback_(guardThis);
}

/******************************************************************** 
Modify : Eric Lee
Date : 2018-01-20
Description : 处理出错事件
*********************************************************************/
void TcpConnection::handleError()
{
  int err = sockets::getSocketError(channel_->fd());
  LOG_ERROR << "TcpConnection::handleError [" << name_
            << "] - SO_ERROR = " << err << " " << strerror_tl(err);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

~青萍之末~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值