muduo_net代码剖析之TcpClient

版权声明:guojawee https://blog.csdn.net/weixin_36750623/article/details/84674215

有了Connector,TcpClient的实现就不难了,它的代码与TcpServer甚至有几分相似,只不过TcpClient只管理一个TcpConnection。先谈几个要点:

  1. TcpClient具备TcpConnection断开之后重新连接的功能,加上Connector具备反复尝试连接的功能,因此客户端和服务器的启动顺序无关紧要。可以先启动客户端,一旦服务器启动,半分钟之内即可恢复连接(由Connector::kMaxRetryDelayMs常数控制);再客户端运行期间服务器可以重启,客户端也会自动重连。
  2. 连接断开后初次重试的延迟时间是随机的,比方说服务器崩溃,它所有的客户端连接同时断开,然后0.5s之后同时再次发起连接,这样既可能造成SYN丢包,也可能给服务器带来短期大负载,影响其服务质量。因此每个TcpClient应该等待一段随机的时间(0.5~2s),再充实,避免拥塞。
  3. 发起连接的时候如果发生TCP SYN丢包,那么系统默认的重试间隔是3s,这期间不会返回错误码,而且这个间隔似乎不容易修改。如果需要缩短间隔,可以再用一个定时器,在0.5s或1s之后发起另一个链接。如果有需求的话,这个功能可以做到Connector中

TcpClient中的成员函数有:

EventLoop* loop_;
ConnectorPtr connector_; //用于主动发起连接
const string name_;

ConnectionCallback connectionCallback_; //连接建立回调函数
MessageCallback messageCallback_;       //消息到来回调函数
WriteCompleteCallback writeCompleteCallback_;//数据发送完毕回调函数

bool retry_;   // 重连,是指连接建立成功之后又断开的时候是否重连
bool connect_; // atomic
int nextConnId_; //name_+nextConnId_用于标识一个连接
mutable MutexLock mutex_;

//connector_连接成功以后,得到一个TcpConnectionPtr
TcpConnectionPtr connection_;

重要的成员函数是connector_(用于发起连接)、connection_(当连接成功建立后,创建TcpConnection对象用于通信)

  1. 构造函数
TcpClient::TcpClient(EventLoop* loop,
                     const InetAddress& serverAddr,
                     const string& nameArg)
  : loop_(CHECK_NOTNULL(loop)),
    connector_(new Connector(loop, serverAddr)),
    name_(nameArg),
    connectionCallback_(defaultConnectionCallback),
    messageCallback_(defaultMessageCallback),
    retry_(false),
    connect_(true),
    nextConnId_(1)
{
  //设置连接成功后的回调函数
  connector_->setNewConnectionCallback(
      std::bind(&TcpClient::newConnection, this, _1));
  // FIXME setConnectFailedCallback
  LOG_INFO << "TcpClient::TcpClient[" << name_
           << "] - connector " << get_pointer(connector_);
}

连接成功后,就会调用自己的成员函数TcpClient::newConnection()函数

void TcpClient::newConnection(int sockfd)
{
  loop_->assertInLoopThread();
  InetAddress peerAddr(sockets::getPeerAddr(sockfd));
  char buf[32];
  snprintf(buf, sizeof buf, ":%s#%d", peerAddr.toIpPort().c_str(), nextConnId_);
  ++nextConnId_;
  string connName = name_ + buf;

  InetAddress localAddr(sockets::getLocalAddr(sockfd));
  //创建一个堆上局部TcpConnection对象,并用TcpClient的智能指针connection_保存起来
  TcpConnectionPtr conn(new TcpConnection(loop_,
                                          connName,
                                          sockfd,
                                          localAddr,
                                          peerAddr));
  //设置各种回调函数
  conn->setConnectionCallback(connectionCallback_); //连接建立
  conn->setMessageCallback(messageCallback_); //可读
  conn->setWriteCompleteCallback(writeCompleteCallback_);//可写
  conn->setCloseCallback( 
      std::bind(&TcpClient::removeConnection, this, _1)); // FIXME: unsafe
  {
    MutexLockGuard lock(mutex_);
    connection_ = conn;
  }
  //使用conn->connectEstablished()内部会关注可读事件
  conn->connectEstablished();
}
void TcpConnection::connectEstablished()
{
  loop_->assertInLoopThread();   //断言处于loop线程
  assert(state_ == kConnecting);   //断言处于未连接状态
  setState(kConnected);   //将状态设置为已连接
 
  channel_->tie(shared_from_this());   //将自身这个TcpConnection对象提升,由于是智能指针,所以不能直接用this
  //shared_from_this()之后引用计数+1,为3,但是shared_from_this()是临时对象,析构后又会减一,
  //而tie是weak_ptr并不会改变引用计数,所以该函数执行完之后引用计数不会更改
  
  channel_->enableReading();   //一旦连接成功就关注它的可读事件,加入到Poller中关注
  
  //回调conn->setConnectionCallback(connectionCallback_)
  connectionCallback_(shared_from_this());
}

下面是连接断开的函数:

void TcpClient::removeConnection(const TcpConnectionPtr& conn)
{
  loop_->assertInLoopThread();
  assert(loop_ == conn->getLoop());
 
  {
    MutexLockGuard lock(mutex_);
    assert(connection_ == conn);
    connection_.reset();   //重置
  }
 
  //I/O线程中销毁
  loop_->queueInLoop(boost::bind(&TcpConnection::connectDestroyed, conn));
  if (retry_ && connect_)  //是否发起重连
  {
    LOG_INFO << "TcpClient::connect[" << name_ << "] - Reconnecting to "
             << connector_->serverAddress().toIpPort();
    //这里的重连是连接成功后断开的重连,所以实际上是重启
    connector_->restart();
  }
}

示例代码

客户端程序的功能:
1.从stdin接受输入,发送给连接的server
2.从网络接收server发来的数据,并打印
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_36750623/article/details/84674215
今日推荐