1. 程式人生 > >muduo_net程式碼剖析之TcpClient

muduo_net程式碼剖析之TcpClient

有了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發來的資料,並列印
在這裡插入圖片描述