muduo源码剖析之TcpClient客户端类

news2024/12/23 14:21:49

简介

muduo用TcpClient发起连接,TcpClient有一个Connector连接器,TCPClient使用Conneccor发起连接, 连接建立成功后, 用socket创建TcpConnection来管理连接, 每个TcpClient class只管理一个TcpConnecction,连接建立成功后设置相应的回调函数。很显然,TcpClient用来管理客户端连接,真正连接交给Connector。

主要成员及属性解析

主要接口

回调setters

这些回调函数会在新连接建立时,通过newConnection内部实现方法传递给TcpConnction对象

核心实现:newConnection

在构造时将这个函数作为回调注册给connector_对象
在Connector中的Channel执行本回调后,创建一个新的TcpConnection对象

connect

调用Connector的start接口

stop

调用Connector的stop接口

主要成员

loop

所属workloop

connector

TcpClient所维护的一个连接器

retry_

重连标志

TcpConnection connection_

TcpClient所维护的一个TCP连接对象
关于连接中回调的传递,参考下面的简图:

eb602a687d5a49408a8209145c038271

源码剖析

代码已编写完整注释,

TcpClient.h

// Copyright 2010, Shuo Chen.  All rights reserved.
// http://code.google.com/p/muduo/
//
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.

// Author: Shuo Chen (chenshuo at chenshuo dot com)
//
// This is a public header file, it must only include public header files.

#ifndef MUDUO_NET_TCPCLIENT_H
#define MUDUO_NET_TCPCLIENT_H

#include "muduo/base/Mutex.h"
#include "muduo/net/TcpConnection.h"

namespace muduo
{
namespace net
{

class Connector;
typedef std::shared_ptr<Connector> ConnectorPtr;

class TcpClient : noncopyable
{
 public:
  // TcpClient(EventLoop* loop);
  // TcpClient(EventLoop* loop, const string& host, uint16_t port);
  TcpClient(EventLoop* loop,
            const InetAddress& serverAddr,
            const string& nameArg);
  ~TcpClient();  // force out-line dtor, for std::unique_ptr members.

  void connect();//请求连接
  void disconnect();//断开连接
  void stop();//停止连接

  TcpConnectionPtr connection() const
  {
    MutexLockGuard lock(mutex_);
    return connection_;
  }

  EventLoop* getLoop() const { return loop_; }
  bool retry() const { return retry_; }
  void enableRetry() { retry_ = true; }

  const string& name() const
  { return name_; }

  /// Set connection callback.
  /// Not thread safe.
  void setConnectionCallback(ConnectionCallback cb)
  { connectionCallback_ = std::move(cb); }

  /// Set message callback.
  /// Not thread safe.
  void setMessageCallback(MessageCallback cb)
  { messageCallback_ = std::move(cb); }

  /// Set write complete callback.
  /// Not thread safe.
  void setWriteCompleteCallback(WriteCompleteCallback cb)
  { writeCompleteCallback_ = std::move(cb); }

 private:
  /// Not thread safe, but in loop
  //新连接建立后的回调函数,将新连接封装为TcpConnection交给TcpClient来管理
  void newConnection(int sockfd);
  /// Not thread safe, but in loop
  释放连接
  void removeConnection(const TcpConnectionPtr& conn);

  //所属loop
  EventLoop* loop_;
  //Connector,用来处理连接阶段,
  ConnectorPtr connector_; // avoid revealing Connector
  const string name_;
  ConnectionCallback connectionCallback_;//连接回调
  MessageCallback messageCallback_;//消息回调
  WriteCompleteCallback writeCompleteCallback_;//数据发送完成回调
  //是否重连
  bool retry_;   // atomic
  //是否连接
  bool connect_; // atomic
  // always in loop thread
  int nextConnId_;
  mutable MutexLock mutex_;
  //管理连接的TcpConnection
  TcpConnectionPtr connection_ GUARDED_BY(mutex_);
};

}  // namespace net
}  // namespace muduo

#endif  // MUDUO_NET_TCPCLIENT_H

TcpClient.cc

// Copyright 2010, Shuo Chen.  All rights reserved.
// http://code.google.com/p/muduo/
//
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.

// Author: Shuo Chen (chenshuo at chenshuo dot com)
//

#include "muduo/net/TcpClient.h"

#include "muduo/base/Logging.h"
#include "muduo/net/Connector.h"
#include "muduo/net/EventLoop.h"
#include "muduo/net/SocketsOps.h"

#include <stdio.h>  // snprintf

using namespace muduo;
using namespace muduo::net;

// TcpClient::TcpClient(EventLoop* loop)
//   : loop_(loop)
// {
// }

// TcpClient::TcpClient(EventLoop* loop, const string& host, uint16_t port)
//   : loop_(CHECK_NOTNULL(loop)),
//     serverAddr_(host, port)
// {
// }

namespace muduo
{
namespace net
{
namespace detail
{

//断开连接
void removeConnection(EventLoop* loop, const TcpConnectionPtr& conn)
{
  loop->queueInLoop(std::bind(&TcpConnection::connectDestroyed, conn));
}

void removeConnector(const ConnectorPtr& connector)
{
  //connector->
}

}  // namespace detail
}  // namespace net
}  // namespace muduo

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新连接建立的回调函数
  connector_->setNewConnectionCallback(
      std::bind(&TcpClient::newConnection, this, _1));
  // FIXME setConnectFailedCallback
  LOG_INFO << "TcpClient::TcpClient[" << name_
           << "] - connector " << get_pointer(connector_);
}

TcpClient::~TcpClient()
{
  LOG_INFO << "TcpClient::~TcpClient[" << name_
           << "] - connector " << get_pointer(connector_);
  TcpConnectionPtr conn;
  bool unique = false;
  {
    MutexLockGuard lock(mutex_);
	//检查所管理对象是否仅由当前 shared_ptr 的实例管理
    unique = connection_.unique();
    conn = connection_;
  }
  if (conn)
  {
    assert(loop_ == conn->getLoop());
    // FIXME: not 100% safe, if we are in different thread
    //执行断开连接操作
    CloseCallback cb = std::bind(&detail::removeConnection, loop_, _1);
    loop_->runInLoop(
        std::bind(&TcpConnection::setCloseCallback, conn, cb));
	
	//如果TcpConnection只有一份,那就强行关闭连接
    if (unique)
    {
      conn->forceClose();
    }
  }
  else
  {
    
    connector_->stop();
    // FIXME: HACK
    loop_->runAfter(1, std::bind(&detail::removeConnector, connector_));
  }
}

//请求连接服务器
void TcpClient::connect()
{
  // FIXME: check state
  LOG_INFO << "TcpClient::connect[" << name_ << "] - connecting to "
           << connector_->serverAddress().toIpPort();
  connect_ = true;
  connector_->start();
}

//断开连接
void TcpClient::disconnect()
{
  connect_ = false;

  {
    MutexLockGuard lock(mutex_);
    if (connection_)
    {
      connection_->shutdown();
    }
  }
}


void TcpClient::stop()
{
  connect_ = false;
  connector_->stop();
}

//新连接建立后的回调函数,将新连接封装为TcpConnection交给TcpClient来管理
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;

  //获取client地址
  InetAddress localAddr(sockets::getLocalAddr(sockfd));
  // FIXME poll with zero timeout to double confirm the new connection
  // FIXME use make_shared if necessary
  //创建一个TcpConnection,并设置相关回调
  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();
}

//释放连接
void TcpClient::removeConnection(const TcpConnectionPtr& conn)
{
  loop_->assertInLoopThread();
  assert(loop_ == conn->getLoop());

  {
    MutexLockGuard lock(mutex_);
    assert(connection_ == conn);
	//释放TcpConnection
    connection_.reset();
  }

  //在所属loop中执行连接断开释放操作
  loop_->queueInLoop(std::bind(&TcpConnection::connectDestroyed, conn));
  //如果设置了重连并且连接标志为true,那就重新连接
  if (retry_ && connect_)
  {
    LOG_INFO << "TcpClient::connect[" << name_ << "] - Reconnecting to "
             << connector_->serverAddress().toIpPort();
    connector_->restart();//重新启动
  }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1189741.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MongoDB副本集特点验证

MongoDB副本集特点验证 mogodb副本集概述副本集搭建副本集结构验证结果源码地址 mogodb副本集概述 MongoDB副本集是将数据同步在多个服务器的过程。 复制提供了数据的冗余备份&#xff0c;并在多个服务器上存储数据副本&#xff0c;提高了数据的可用性&#xff0c; 并可以保证…

第一章:IDEA使用介绍

系列文章目录 文章目录 系列文章目录前言一、IDEA 的使用1.1 IDEA 工作界面1.2 IDEA 的基本介绍和使用1.3 IDEA 使用技巧和经验1.4 IDEA编译与源文件1.5 IDEA 常用快捷键1.6 IDEA模板/自定义模板 总结 前言 IDEA 全称 IntelliJ IDEA&#xff0c;在业界被公认为最好的 Java 开发…

经验模态分解(Empirical Mode Decomposition,EMD)(附代码)

代码原理 EMD&#xff08;Empirical Mode Decomposition&#xff09;&#xff0c;也称为经验模态分解&#xff0c;是一种将非线性和非平稳信号分解成多个本征模态函数&#xff08;Intrinsic Mode Functions&#xff0c;简称IMF&#xff09;的方法。 EMD的基本原理是通过一系列…

算法进阶指南图论 道路与航线

其实再次看这题的时候。想法就是和强连通分量有关&#xff0c;我们很容易发现&#xff0c;题目中所说的双向边&#xff0c;就构成了一个强连通分量&#xff0c;而所谓的单向边&#xff0c;则相当于把强连通分量进行缩点&#xff0c;然后整个图成为了一个DAG&#xff0c;众所周知…

把自己本地项目发布到Gitee

目录 1.准备工作 ​2.gitee创建仓库 3.本地上传代码 4.验证​ 1.准备工作 本地安装了git&#xff0c;公钥私钥都配置好了 2.gitee创建仓库 创建仓库&#xff0c;没有仓库放不了代码 只需要选择分支类型&#xff0c;和带星号的 进入下一页 点这个 3.本地上传代码 新建一…

vivado时序分析-1

AMD Vivado ™ 集成设计环境 (IDE) 提供了多项报告命令 &#xff0c; 用于验证设计是否满足所有时序约束 &#xff0c; 以及是否准备好加载到应用开发板上。“Report Timing Summary ” &#xff08; 时序汇总报告 &#xff09; 属于时序验收报告 &#xff0c; 等同于 ISE De…

链表OJ题(1)

今天讲解两道链表OJ题目。 1.链表的中间节点 给你单链表的头结点 head &#xff0c;请你找出并返回链表的中间结点。 如果有两个中间结点&#xff0c;则返回第二个中间结点。 示例 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[3,4,5] 解释&#xff1a;链表只有一个…

1214. 波动数列

题目&#xff1a; 1214. 波动数列 - AcWing题库 思路&#xff1a;dp dp划分递归 转自&#xff1a; AcWing 1214. 波动数列&#xff08;有公式详细推导&#xff09; - AcWing 代码&#xff1a; #include <iostream> #include <cstring> #include <algori…

Java学习 8.Java-递归

一、递归的概念 引例&#xff1a; 一个方法在执行过程中调用自身&#xff0c;就称为递归&#xff08;函数自己调用自己&#xff09; 递归相当于数学的数学归纳法&#xff0c;有一个起始条件&#xff0c;有一个递推公式 递归的必要条件 1.将原问题划分为子问题&#xff0c;…

介绍两个好用又好玩的大模型工具

先让数字人跟大家打个招呼吧。 我的AI数字人会手语了 发现没&#xff0c;我的数字人本周又学了一个新技能&#xff1a;手语。 这些数字人都是通过AI生成的。 但数字人不是今天的主题&#xff0c;今天要跟大家聊聊大模型。 自从大模型出现后&#xff0c;很多人&#xff08;包…

2022年接口测试总结【温故知新系列】

本文主要分为两个部分&#xff1a; 第一部分&#xff1a;主要从问题出发&#xff0c;引入接口测试的相关内容并与前端测试进行简单对比&#xff0c;总结两者之前的区别与联系。但该部分只交代了怎么做和如何做&#xff1f;并没有解释为什么要做&#xff1f; 第二部分&#xff1…

Centos批量删除系统重复进程

原创作者&#xff1a;运维工程师 谢晋 Centos批量删除系统重复进程 客户一台CENTOS 7系统负载高&#xff0c;top查看有很多sh的进程&#xff0c;输入命令top -c查看可以看到对应的进程命令是/bin/bash     经分析后发现是因为该脚本执行时间太长&#xff0c;导致后续执…

机器学习——奇异值分解案例(图片压缩未开始)

本想大迈步进入前馈神经网络 但是…唉…瞅了几眼&#xff0c;头晕 然后想到之前梳理的奇异值分解、主成分分析、CBOW都没有实战 如果没有实际操作&#xff0c;会有一种浮在云端的虚无感 但是如果要实际操作&#xff0c;我又不想直接调用库包 可是…如果不直接调包&#xff0c;感…

【深度学习】可交互讲解图神经网络GNN

在正式开始前&#xff0c;先找准图神经网络GNN(Graph Neural Network)的位置。 图神经网络GNN是深度学习的一个分支。 深度学习的四个分支对应了四种常见的数据格式&#xff0c;前馈神经网络FNN处理表格数据&#xff0c;表格数据可以是特征向量&#xff0c;卷积神经网络CNN处理…

基于SSM的楼房销售系统设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

腾讯蒋杰:坚持用技术服务应用,腾讯混元大模型已接入180多个业务

“腾讯正在持续探索大模型应用场景&#xff0c;目前内部超180项业务已经接入腾讯混元大模型进行内测”&#xff0c;11月9日&#xff0c;腾讯集团副总裁蒋杰在2023年世界互联网大会乌镇峰会上表示。 腾讯集团副总裁 蒋杰 作为腾讯全链路自研的实用级大模型&#xff0c;自2023年…

软件测试工程师的分支有哪些?

各行各业都有很多分支&#xff0c;软件测试的细分领域又有哪些呢&#xff1f; 拆解来说&#xff1a; 功能测试的渠道有&#xff0c;app&#xff0c;小程序&#xff0c;web/h5。其中app和web要考虑兼容性。app需要考虑安卓&#xff0c;鸿蒙&#xff0c;ios这三个系统&#xff0…

使用xlwings获取excel表的行和列以及指定单元格的值

import xlwings as xw app xw.App(visibleFalse) # 隐藏Excel wb app.books.open(文档1.xlsx) # 打开工作簿sht wb.sheets[Sheet1] # 实例化工作表1#获取行数和列数 rows_countsht.range(1, 1).expand().shape[0] cols_countsht.range(1, 1).expand().shape[1] print(row…

设计模式JAVA

1 创建型 如何合理的创建对象&#xff1f; 1.1 单例模式 字面意思就是只能创建一个对象实例时使用。 例如&#xff0c;Windows中只能打开一个任务管理器&#xff0c;这样可以避免因打开多个任务管理器窗口而造成内存资源的浪费&#xff0c;或出现各个窗口显示内容的不一致等…