当前位置: 首页 > news >正文

TcpClinet

1. 时序图(Mermaid)

正常连接流程

User CodeTcpClientConnectorEventLoopChannelSocketTcpConnectionconnect()start()runInLoop(startInLoop)startInLoop()createNonblockingOrDie()connect() [EINPROGRESS]new Channel(sockfd)setWriteCallback(handleWrite)enableWriting()Wait for socket writablehandleEvent()handleWrite()getsockopt(SO_ERROR)newConnectionCallback_(sockfd)newConnection(sockfd)conn = TcpConnection(sockfd)connectEstablished()connectionCallback_(conn)User CodeTcpClientConnectorEventLoopChannelSocketTcpConnection

连接失败重试流程

User CodeTcpClientConnectorEventLoopSocketstart()runInLoop(startInLoop)startInLoop()connect() [ECONNREFUSED]retry(sockfd)runAfter(0.5s, startInLoop)Wait 0.5sstartInLoop() [2nd attempt]connect() [ECONNREFUSED]retry(sockfd)runAfter(1s, startInLoop)Wait 1s, backoff: 0.5s ->> 1s ->> 2s ->> ...User CodeTcpClientConnectorEventLoopSocket

断开连接流程

User CodeTcpClientConnectorTcpConnectionEventLoopstop()stop()queueInLoop(stopInLoop)disconnect()shutdown()shutdown & closeCallbackremoveConnection(conn)queueInLoop(connectDestroyed)stopInLoop()removeAndResetChannel()queueInLoop(resetChannel)alt[Connection exists][No connection (still retrying)]User CodeTcpClientConnectorTcpConnectionEventLoop

2. 详细函数调用说明

流程 A:成功连接

Step 1: TcpClient::connect()
void TcpClient::connect()
{LOG_INFO << "TcpClient::connect[" << name_ << "] - connecting to "<< connector_->serverAddress().toIpPort();connect_ = true;           // 标记为"要连接"connector_->start();        // 启动连接器
}

作用:设置标志位,告诉 Connector 开始尝试连接。


Step 2: Connector::start()
void Connector::start()
{connect_ = true;loop_->runInLoop(std::bind(&Connector::startInLoop, this));
}

作用

  • 设置 connect_ 标志为 true(允许连接)
  • startInLoop() 任务放入 EventLoop 的队列,确保在 IO 线程中执行

Step 3: Connector::startInLoop()(在 EventLoop 线程中执行)
void Connector::startInLoop()
{loop_->assertInLoopThread();assert(state_ == kDisconnected);if (connect_){connect();    // 真正开始连接}else{LOG_DEBUG << "do not connect";}
}

作用:检查 connect_ 标志,如果为真则调用 connect()


Step 4: Connector::connect()
void Connector::connect()
{int sockfd = sockets::createNonblockingOrDie(serverAddr_.family());// 创建非阻塞 socketint ret = sockets::connect(sockfd, serverAddr_.getSockAddr());// 尝试连接(会立即返回,因为是非阻塞)int savedErrno = (ret == 0) ? 0 : errno;switch (savedErrno){case 0:case EINPROGRESS:           // 连接正在进行case EINTR:case EISCONN:               // 已连接connecting(sockfd);     // 进入"连接中"状态break;case EAGAIN:case ECONNREFUSED:          // 连接被拒绝// ...retry(sockfd);          // 重试break;case EACCES:case EPERM:// ... 严重错误LOG_SYSERR << "connect error";sockets::close(sockfd);break;}
}

作用

  • 创建非阻塞 socket
  • 尝试连接到服务器
  • 根据返回值(errno)决定下一步:
    • EINPROGRESS:连接正在进行 → 进入 connecting() 等待可写
    • 连接失败(如 ECONNREFUSED)→ 进入 retry() 重试
    • 严重错误 → 关闭 socket

Step 5: Connector::connecting(int sockfd)
void Connector::connecting(int sockfd)
{setState(kConnecting);      // 状态改为"连接中"assert(!channel_);channel_.reset(new Channel(loop_, sockfd));// 设置回调:当 socket 可写时,调用 handleWritechannel_->setWriteCallback(std::bind(&Connector::handleWrite, this));channel_->setErrorCallback(std::bind(&Connector::handleError, this));// 开启"可写"事件监听channel_->enableWriting();
}

作用

  • 创建 Channel 对象来管理这个 socket 的事件
  • 注册 socket 的写事件监听(当 socket 可写时,表示连接已建立或失败)
  • EventLoop 的 poller(epoll/poll)会监控这个 socket

Step 6: Channel::handleEvent()(当 socket 可写时触发)

这个函数由 EventLoop 的 poller 调用,最终会调用:

Step 7: Connector::handleWrite()
void Connector::handleWrite()
{LOG_TRACE << "Connector::handleWrite " << state_;if (state_ == kConnecting){int sockfd = removeAndResetChannel();// 获取 socket 的错误状态int err = sockets::getSocketError(sockfd);if (err){// 连接失败LOG_WARN << "Connector::handleWrite - SO_ERROR = " << err;retry(sockfd);  // 重试连接}else{// 连接成功!setState(kConnected);// 调用上层的回调newConnectionCallback_(sockfd);}}else{LOG_ERROR << "Connector::handleWrite - state error";}
}

作用

  • 检查 socket 的连接状态(通过 SO_ERROR
  • 如果成功:状态改为 kConnected,调用 newConnectionCallback_(sockfd)
  • 如果失败:调用 retry(sockfd) 进行重试

Step 8: TcpClient::newConnection(int sockfd)(回调函数)
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 对象来管理这个连接**TcpConnectionPtr conn(new TcpConnection(loop_,connName,sockfd,localAddr,peerAddr));// 将用户设置的各种回调注册到 TcpConnectionconn->setConnectionCallback(connectionCallback_);conn->setMessageCallback(messageCallback_);conn->setWriteCompleteCallback(writeCompleteCallback_);conn->setCloseCallback(std::bind(&TcpClient::removeConnection, this, _1));{MutexLockGuard lock(mutex_);connection_ = conn;  // 保存连接}// 通知 TcpConnection:连接已建立conn->connectEstablished();
}

作用

  • 创建 TcpConnection 对象来管理这个连接
  • 将用户的回调(如 connectionCallback_)注册到 TcpConnection
  • 调用 connectEstablished() 来初始化连接并触发用户的 connectionCallback_

流程 B:连接失败与重试

Connector::retry(int sockfd)
void Connector::retry(int sockfd)
{sockets::close(sockfd);     // 关闭失败的 socketsetState(kDisconnected);    // 状态改回"未连接"if (connect_)  // 如果仍然要连接{LOG_INFO << "Connector::retry - Retry connecting to " << serverAddr_.toIpPort()<< " in " << retryDelayMs_ << " milliseconds.";// **关键:延迟后再次尝试连接**loop_->runAfter(retryDelayMs_ / 1000.0,std::bind(&Connector::startInLoop, shared_from_this()));// **backoff:重试间隔逐倍增长(0.5s -> 1s -> 2s -> ... -> 30s)**retryDelayMs_ = std::min(retryDelayMs_ * 2, kMaxRetryDelayMs);}
}

作用

  • 关闭失败的 socket
  • 如果 connect_ 标志仍为 true,就使用 runAfter() 延迟后再次调用 startInLoop()
  • 每次重试前增加延迟时间(指数退避)

流程 C:停止与断开

TcpClient::stop()
void TcpClient::stop()
{connect_ = false;           // 标记为"不要连接"connector_->stop();         // 停止连接器
}
Connector::stop()
void Connector::stop()
{connect_ = false;   // 设置标志为 falseloop_->queueInLoop(std::bind(&Connector::stopInLoop, this));// 在 EventLoop 中执行 stopInLoop
}
Connector::stopInLoop()
void Connector::stopInLoop()
{loop_->assertInLoopThread();if (state_ == kConnecting){setState(kDisconnected);int sockfd = removeAndResetChannel();  // 移除 Channel,关闭 socketretry(sockfd);  // 尝试调用 retry// 但因为 connect_ 已经是 false,retry 中不会再安排新的连接}
}

流程 D:连接建立后的断开

TcpClient::disconnect()
void TcpClient::disconnect()
{connect_ = false;{MutexLockGuard lock(mutex_);if (connection_){connection_->shutdown();  // 优雅关闭连接}}
}
TcpClient::removeConnection(const TcpConnectionPtr& conn)
void TcpClient::removeConnection(const TcpConnectionPtr& conn)
{loop_->assertInLoopThread();assert(loop_ == conn->getLoop());{MutexLockGuard lock(mutex_);assert(connection_ == conn);connection_.reset();  // 清空连接指针}// 异步销毁连接loop_->queueInLoop(std::bind(&TcpConnection::connectDestroyed, conn));// 如果启用了自动重连,则重新连接if (retry_ && connect_){LOG_INFO << "TcpClient::connect[" << name_ << "] - Reconnecting to "<< connector_->serverAddress().toIpPort();connector_->restart();  // 重新启动连接器}
}

作用

  • 移除已保存的连接指针
  • 异步销毁 TcpConnection
  • 如果启用自动重连(retry_ = true),则自动重新连接

总结

阶段关键函数状态说明
1TcpClient::connect()connect_=true用户发起连接请求
2Connector::start()-startInLoop 放入任务队列
3Connector::startInLoop()state_=kConnecting真正开始连接
4Connector::connect()state_=kConnecting创建 socket,发起非阻塞连接
5Connector::connecting()state_=kConnecting将 socket 的写事件加入 poller
6Connector::handleWrite()state_=kConnected连接成功,调用上层回调
7TcpClient::newConnection()-创建 TcpConnection 对象
8TcpConnection::connectEstablished()-触发用户的 connectionCallback_
http://www.dtcms.com/a/532049.html

相关文章:

  • Appium+Python+Android+Nodejs环境安装
  • SCDN:互联网时代网站安全的安全保障
  • Linux小课堂: Apache服务在CentOS上的安装与基础配置指南
  • Gorm(八)预加载方式
  • 网站开发与设计的实训场地WordPress无法自动推送
  • 【找指针数组最大值】2022-11-24
  • 自己做网站能赚钱吗做spa的网站怎么推广
  • 网络管理中的名词
  • gitlab配置git的ssh秘钥
  • 机器狗进化论:当“园区跑腿”遇上具身智能,一场静悄悄的变革正在发生
  • 江宁区建设工程质量监督站网站学校网站源码开源
  • Docker LXC深度解析:从基础概念到实战演练
  • Spring Boot3零基础教程,docker 批量安装软禁,笔记68
  • 【C语言】函数栈帧的创建和销毁
  • 架构的尺度:从单机到分布式,服务端技术的深度演进
  • 优秀国内个人网站网址网站设计需要那些模块
  • 【ARM驱动】【FreeROTS移植到ARM驱动平台介绍】
  • BELLE中的表1
  • go-ethereum core之交易索引txIndexer
  • 描述对于营销型网站建设很重要飘红效果更佳信阳做网站 汉狮网络
  • 油猴脚本学习1——元数据头部
  • mysql 如何让事件执行
  • PantherX2 debain/armbian Jellyfin10.10.7升级10.11启动后无法监听端口8096的解决办法
  • 网站建设利弊中山币做网站公司
  • Kaleidoscope for mac 文件对比工具
  • LeetCode 1901.寻找峰值2
  • 沈阳建设网站费用北京网站优化软件
  • 【Android】【底层原理】深入解析SELinux模块
  • 阮一峰《TypeScript 教程》学习笔记——注释指令
  • 最好的开发网站建设价格app免费制作平台下载