C++ 全栈知识体系C++ 全栈知识体系
✿导航
  • 基础
  • 函数
  • 知识点
  • IO框架
  • 新版本特性
  • 数据库原理
  • SQL语言
  • SQL - MySQL
  • NoSQL - Redis
  • NoSQL - ElasticSearch
  • 算法基础
  • 常见算法
  • 领域算法
  • 分布式算法
  • 数据结构与算法
  • 计算机网络
  • 操作系统
  • 计算机组成
  • 开发
  • 测试
  • 架构基础
  • 分布式系统
  • 微服务
  • 中间件
  • 概念
  • 理论
  • 架构设计原则
  • 设计模式
  • 协议
  • 技术选型
  • 编码规范
  • 流水线构建 - CI/CD
  • 知识点 - Linux
  • 网站 - Nginx
  • 容器化 - Docker
  • 容器编排 - Kubernetes
  • 服务网格 - Service Mesh Istio
  • 常用快捷键 - Shortcut
  • 工具使用 - Tools
  • 开源项目
  • 学习项目
  • 个人项目
  • 项目开发
  • 项目Idea
  • 并发
  • 部署
  • 分布式
  • 知识
  • 问题
  • 编程语言与技术
  • 系统与架构
  • 软件开发实践
  • 数据处理与应用设计
  • 个人
  • 产品
  • 团队
  • 知识体系
  • Vue
关于
✿导航
  • 基础
  • 函数
  • 知识点
  • IO框架
  • 新版本特性
  • 数据库原理
  • SQL语言
  • SQL - MySQL
  • NoSQL - Redis
  • NoSQL - ElasticSearch
  • 算法基础
  • 常见算法
  • 领域算法
  • 分布式算法
  • 数据结构与算法
  • 计算机网络
  • 操作系统
  • 计算机组成
  • 开发
  • 测试
  • 架构基础
  • 分布式系统
  • 微服务
  • 中间件
  • 概念
  • 理论
  • 架构设计原则
  • 设计模式
  • 协议
  • 技术选型
  • 编码规范
  • 流水线构建 - CI/CD
  • 知识点 - Linux
  • 网站 - Nginx
  • 容器化 - Docker
  • 容器编排 - Kubernetes
  • 服务网格 - Service Mesh Istio
  • 常用快捷键 - Shortcut
  • 工具使用 - Tools
  • 开源项目
  • 学习项目
  • 个人项目
  • 项目开发
  • 项目Idea
  • 并发
  • 部署
  • 分布式
  • 知识
  • 问题
  • 编程语言与技术
  • 系统与架构
  • 软件开发实践
  • 数据处理与应用设计
  • 个人
  • 产品
  • 团队
  • 知识体系
  • Vue
关于
  • 编程语言与技术

    • Effective C++: 改善程序与设计的55个具体做法

      • 第2章 - 构造/析构/赋值运算(一)
      • 第2章 - 构造/析构/赋值运算(二)
      • 第2章 - 构造/析构/赋值运算(三)
      • 第3章 - 资源管理
      • 第4章 - 设计与声明(一)
      • 第4章 - 设计与声明(二)
      • 第5章 - 实现(一)
      • 第5章 - 实现(二)
      • 第6章 - 继承与面向对象设计
      • 第7章 - 模板与泛型编程
    • 深度探索C++对象模型

      • 第1章 - 关于对象
      • 第2章 - 构造函数语意学
      • 第3章 - Data 语意学
    • STL源码剖析

      • 第1章 - STL概论和版本简介
      • 第2章 - 空间配置器
      • 第3章 - 迭代器(iterators)概念与traits编程技法(一)
      • 第3章 - 迭代器(iterators)概念与traits编程技法(二)
      • 第4章 - 序列式容器 vector
      • 第4章 - 序列式容器 list
      • 第4章 - 序列式容器 deque
      • 第4章 - 序列式容器 stack和queue
      • 第4章 - 序列式容器 heap
      • 第4章 - 序列式容器 priority_queue
      • 第4章 - 序列式容器 slist
      • 第5章 - 关联式容器 RB-tree
      • 第5章 - 关联式容器 set和map
      • 第5章 - 关联式容器 hashtable
      • 第6章 - 算法
      • 第6章 - 算法之set
      • 第7章 - 仿函数
      • 第8章 - 配接器
  • 系统与架构

    • 深入理解计算机系统

      • 第1章 - 计算机系统漫游
      • 第2章 - 信息的表示和处理
      • 第3章 - 程序的机器级表示
      • 第5章 - 优化程序性能
      • 第6章 - 存储器层次结构
      • 第7章 - 链接
      • 第8章 - 异常控制流
      • 第9章 - 虚拟内存
      • 第10章 - 系统级I/O
      • 第11章 - 网络编程
      • 第12章 - 并发编程
    • 大型网站技术架构——核心原理与案例分析

      • 第1章 - 大型网站架构演化
      • 第2章 - 大型网站架构模式
      • 第3章 - 大型网站核心架构要素
      • 第4章 - 瞬时响应:网站的高性能架构
      • 第5章 - 万无一失:网站的高可用架构
      • 第6章 - 永无止境:网站的伸缩性架构
      • 第7章 - 随需应变:网站的可扩展架构
      • 第8章 - 固若金汤:网站的安全架构
    • 从零开始学架构

      • 架构基础
      • 架构设计原则
      • 高性能架构
      • 高可用架构
    • 程序员的自我修养————链接、装载与库

      • 第1章 - 简介
      • 第2章 - 静态链路
      • 第3章 - 目标文件里有什么
      • 第4章 - 静态链接
      • 第7章 - 动态链接
      • 第8章 - 共享库版本
      • 第10章 - 内存
      • 第11章 - 运行库
      • 第12章 - 系统调用与API
      • 第13章 - 运行库实现
  • 软件开发实践

    • 重构改善既有代码的设计

      • 第1章 - 重构,第一个示例
      • 第2章 - 重构的原则
      • 第3章 - 代码的坏味道
      • 第5章 - 重构列表
      • 第6章 - 重新组织函数
      • 第7章 - 在对象之间搬移特性
      • 第8章 - 重新组织数据
      • 第9章 - 简化条件表达式
      • 第10章 - 简化函数调用
      • 第11章 - 处理概括关系
      • 第12章 - 设计之大型重构
    • 代码大全2

      • 第1章 - 欢迎进入软件构建的世界
      • 第2章 - 用隐喻来更充分地理解软件开发
      • 第3章 - 三思而后行: 前期准备
      • 第4章 - 关键的构建决策
      • 第5章 - 软件构建中的设计
    • Linux多线程服务端编程——使用muduo C++ 网络库

      • Buffer类的设计
      • 设计与实现
      • 定时器与TimerQueue
      • Protobuf网络传输和Protobuf编解码器与消息分发器
      • EventLoop类剖析
      • EventLoopThread和EventLoopThreadPool剖析
      • TCP网络库和核心类
      • Connector剖析
      • TcpClient剖析
      • 学习总结
      • timing wheel
      • 消息广播服务
      • 线程安全的对象生命期管理
  • 数据处理与应用设计

    • 数据密集型应用系统设计

      • 第1章 - 可靠、可扩展与可维护的应用系统
      • 第2章 - 数据模型与查询语言
      • 第3章 - 数据存储与检索
      • 第4章 - 数据编码与演化
      • 第5章 - 数据复制
      • 第6章 - 数据分区
      • 第7章 - 事务

muduo - Connector剖析

    ​# Connector剖析

    Connector负责主动发起连接,不负责创建socket,只负责连接的建立,外部调用Connector::start就可以发起连接,Connector具有重连的功能和停止连接的功能,连接成功建立后返回到TcpClient。

    与Acceptor 相比少了一个 acceptSocket_ 成员,因为Connector 是创建一个新的sockfd 并connect 它,创建过程如下:Connector::start()-->Connector::startInLoop()-->void Connector::connect()。

    void Connector::connect()
    {
      int sockfd = sockets::createNonblockingOrDie(serverAddr_.family()); // 创建非阻塞套接字
      int ret = sockets::connect(sockfd, serverAddr_.getSockAddr());
      int savedErrno = (ret == 0) ? 0 : errno;
      switch (savedErrno)
      {
        case 0:
        case EINPROGRESS:    // 非阻塞套接字,未连接成功返回码是EINPROGRESS表示正在连接
        case EINTR:
        case EISCONN:        // 连接成功
          connecting(sockfd); 
          break;
        ...
      }
    }
    

    再看Connector::connecting():

    void Connector::connecting(int sockfd)
    {
      setState(kConnecting);
      assert(!channel_);
      channel_.reset(new Channel(loop_, sockfd));// Channel与sockfd关联
      // 设置可写回调函数,这时候如果socket没有错误,sockfd就处于可写状态
      channel_->setWriteCallback(
          std::bind(&Connector::handleWrite, this)); // FIXME: unsafe
      // 设置错误回调函数
      channel_->setErrorCallback(
          std::bind(&Connector::handleError, this)); // FIXME: unsafe
    
      // channel_->tie(shared_from_this()); is not working,
      // as channel_ is not managed by shared_ptr
      channel_->enableWriting();    // 让Poller关注可写事件
    }
    

    现在connnect(sockfd) 没有出错,sockfd 就处于可写状态(内核缓冲区不为满),而且poller 关注了可写事件,触发调用Connector::handleWrite()。

    void Connector::handleWrite()
    {
        LOG_TRACE <<  "Connector::handleWrite " << state_;
    
         if (state_ == kConnecting)
        {
             int sockfd = removeAndResetChannel();    // 从poller中移除关注,并将channel置空
             // socket可写并不意味着连接一定建立成功
             // 还需要用getsockopt(sockfd, SOL_SOCKET, SO_ERROR, ...)再次确认一下。
             int err = sockets::getSocketError(sockfd);
            ......
             else     // 连接成功
            {
                setState(kConnected);
                 if (connect_)
                {
                    newConnectionCallback_(sockfd);      // 回调
                }
    
            }
        }
    }
    

    在handleWrite()里面需要removeAndResetChannel(),因此此时连接建立,故不用再关注channel的可写事件,最终会执行 channel_.reset();  即把channel析构了。此外函数需要返回sockfd, 让TcpConnection来接管。

    连接成功后调用newConnectionCallback_(sockfd),调用预先设置的回调函数。实际上 Connector 一般也不单独使用,作为TcpClient 的成员,但TcpClient 与 TcpServer 不同的是,它只有一个TcpConnection 成员。

    TcpServer: 
    typedef std::map<string, TcpConnectionPtr> ConnectionMap;
    std::unique_ptr<Acceptor> acceptor_; // avoid revealing Acceptor
    ConnectionMap connections_;
    
    TcpClient:
    ConnectorPtr connector_; 
    TcpConnectionPtr connection_ GUARDED_BY(mutex_);
    

    即一个TcpClient 对应一个TcpConnection 和一个 Connector;而一个TcpServer 对应一个TcpConnection 列表 和 一个 Acceptor。

    测试程序:

    ///home/muduo/muduo_recipes/recipes/reactor/s11/test12.cc
    #include "Connector.h"
    #include "EventLoop.h"
    
    #include <stdio.h>
    
    muduo::EventLoop* g_loop;
    
    void connectCallback(int sockfd)
    {
      printf("connected.\n");
      g_loop->quit();
    }
    
    int main(int argc, char* argv[])
    {
      muduo::EventLoop loop;
      g_loop = &loop;
      muduo::InetAddress addr("127.0.0.1", 9981);
      muduo::ConnectorPtr connector(new muduo::Connector(&loop, addr));
      connector->setNewConnectionCallback(connectCallback);
      connector->start();
    
      loop.loop();
    }
    

    执行结果:

    服务端:
    [root@192 s04]# ./test7
    main(): pid = 2294
    newConnection(): accepted a new connection from 127.0.0.1:43836
    
    客户端:
    [root@192 s11]# ./test12
    20201201 23:23:51.688777Z  2295 DEBUG Connector ctor[0x822360] - Connector.cc:31
    connected.
    20201201 23:23:51.689096Z  2295 DEBUG ~Connector dtor[0x822360] - Connector.cc:36
    
    Last Updated:
    Contributors: klc407073648
    Prev
    TCP网络库和核心类
    Next
    TcpClient剖析