muduo源码剖析之Connector客户端连接类

简介

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

主要成员及属性解析

主要接口

setNewConnectionCallback

设置TcpClient交给的回调函数

start

最后通过loop的runInLoop调用
调用connect内部实现

stop

最终通过loop的queueInLoop调用
回收Channel控制的套接字(如果有的话)
设置connect_标记为false

retry

若connect_标记为true,则重连

核心实现:connect

调用Socket::connect方法连接服务端
连接成功后,创建一个Channel
将自身的handleWrite回调注册到Channel上,并激活可写事件关注

核心实现:handleWrite

根据getSockError的情况决定调用创建连接回调,或是错误回调,或retry操作
其中包含了TcpClient注册的回调newConnection

主要成员

loop

所属的workloop

channel

unique_ptr指针,仅在连接建立时动态创建Channel对象
当channel触发可写事件时,执行handleWrite
并在handleWrite中执行TcpClient的newConnection

serverAddr

服务端的inetaddr地址信息

connect_

非常重要的一个标记,决定了是否retry

源码剖析

代码已编写完整注释,

Connector.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 an internal header file, you should not include this.#ifndef MUDUO_NET_CONNECTOR_H
#define MUDUO_NET_CONNECTOR_H#include "muduo/base/noncopyable.h"
#include "muduo/net/InetAddress.h"#include <functional>
#include <memory>namespace muduo
{
namespace net
{class Channel;
class EventLoop;//负责主动发起连接,不负责创建socket,只负责连接的建立
class Connector : noncopyable,public std::enable_shared_from_this<Connector>
{public:typedef std::function<void (int sockfd)> NewConnectionCallback;Connector(EventLoop* loop, const InetAddress& serverAddr);~Connector();//设置新连接到来时的回调函数void setNewConnectionCallback(const NewConnectionCallback& cb){ newConnectionCallback_ = cb; }void start();  // can be called in any threadvoid restart();  // must be called in loop threadvoid stop();  // can be called in any thread//返回服务器的地址信息const InetAddress& serverAddress() const { return serverAddr_; }private:enum States { kDisconnected, kConnecting, kConnected };static const int kMaxRetryDelayMs = 30*1000;static const int kInitRetryDelayMs = 500;//设置连接状态void setState(States s) { state_ = s; }//在loop中执行的start启动操作void startInLoop();//在loop中执行的stop暂停操作void stopInLoop();//请求连接服务器void connect();//连接服务器成功后设置channel事件void connecting(int sockfd);//写事件回调void handleWrite();//错误发生回调void handleError();//重连void retry(int sockfd);//移除并释放channelint removeAndResetChannel();//释放channelvoid resetChannel();//所属的EventLoopEventLoop* loop_;//要连接的server地址InetAddress serverAddr_;//是否连接服务器标志bool connect_; // atomic//连接状态States state_;  // FIXME: use atomic variable//对应的channelstd::unique_ptr<Channel> channel_;//连接成功时的回调函数NewConnectionCallback newConnectionCallback_;//重连间隔时间int retryDelayMs_;
};}  // namespace net
}  // namespace muduo#endif  // MUDUO_NET_CONNECTOR_H

Connector.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)
//#include "muduo/net/Connector.h"#include "muduo/base/Logging.h"
#include "muduo/net/Channel.h"
#include "muduo/net/EventLoop.h"
#include "muduo/net/SocketsOps.h"#include <errno.h>using namespace muduo;
using namespace muduo::net;const int Connector::kMaxRetryDelayMs;Connector::Connector(EventLoop* loop, const InetAddress& serverAddr): loop_(loop),serverAddr_(serverAddr),connect_(false),state_(kDisconnected),retryDelayMs_(kInitRetryDelayMs)
{LOG_DEBUG << "ctor[" << this << "]";
}Connector::~Connector()
{LOG_DEBUG << "dtor[" << this << "]";assert(!channel_);
}void Connector::start()
{connect_ = true;//在所属IO线程Lloop中调用该函数,连接服务器loop_->runInLoop(std::bind(&Connector::startInLoop, this)); // FIXME: unsafe
}void Connector::startInLoop()
{loop_->assertInLoopThread();assert(state_ == kDisconnected);if (connect_){connect();//请求连接}else{LOG_DEBUG << "do not connect";}
}void Connector::stop()
{connect_ = false;loop_->queueInLoop(std::bind(&Connector::stopInLoop, this)); // FIXME: unsafe// FIXME: cancel timer
}void Connector::stopInLoop()
{loop_->assertInLoopThread();if (state_ == kConnecting){//将状态设置为断开连接setState(kDisconnected);//移除并释放channelint sockfd = removeAndResetChannel();retry(sockfd);}
}//连接服务器
void Connector::connect()
{//创建一个非阻塞的socket fdint sockfd = sockets::createNonblockingOrDie(serverAddr_.family());//请求连接服务器int 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 EADDRINUSE:case EADDRNOTAVAIL:case ECONNREFUSED:case ENETUNREACH:retry(sockfd);//重连break;case EACCES:case EPERM:case EAFNOSUPPORT:case EALREADY:case EBADF:case EFAULT:case ENOTSOCK:LOG_SYSERR << "connect error in Connector::startInLoop " << savedErrno;sockets::close(sockfd);break;default:LOG_SYSERR << "Unexpected error in Connector::startInLoop " << savedErrno;sockets::close(sockfd);// connectErrorCallback_();break;}
}void Connector::restart()
{loop_->assertInLoopThread();//设置标志为断开连接setState(kDisconnected);retryDelayMs_ = kInitRetryDelayMs;connect_ = true;//启动startInLoop();
}//连接服务器成功后设置channel事件
void Connector::connecting(int sockfd)
{setState(kConnecting);//设置状态为正在连接assert(!channel_);//创建一个channelchannel_.reset(new Channel(loop_, 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();
}//移除并释放channel
int Connector::removeAndResetChannel()
{//取消所有事件的监听channel_->disableAll();//将channel从loop中移除channel_->remove();//释放channelint sockfd = channel_->fd();// Can't reset channel_ here, because we are inside Channel::handleEventloop_->queueInLoop(std::bind(&Connector::resetChannel, this)); // FIXME: unsafereturn sockfd;
}void Connector::resetChannel()
{//释放unique_ptr所指向的资源,也就是channelchannel_.reset();
}//可写事件的回调函数
void Connector::handleWrite()
{LOG_TRACE << "Connector::handleWrite " << state_;if (state_ == kConnecting){//移除channel(Connector的channel只管理建立连接的阶段),成功建立连接后//交给TcpClient的TcpConnection来管理int sockfd = removeAndResetChannel();//可写并不一定连接建立成功//如果连接发生错误,socket会是可读可写的//所以还需要调用getsockopt检查是否出错int err = sockets::getSocketError(sockfd);if (err){LOG_WARN << "Connector::handleWrite - SO_ERROR = "<< err << " " << strerror_tl(err);retry(sockfd);//出错重连}else if (sockets::isSelfConnect(sockfd))//是否是自连接{LOG_WARN << "Connector::handleWrite - Self connect";retry(sockfd);}else{//连接成功建立,更改状态//调用TcpClient设置的回调函数,创建TcpConnection对象	setState(kConnected);//设置状态为已经成功连接if (connect_){newConnectionCallback_(sockfd);}else{sockets::close(sockfd);}}}else{// what happened?assert(state_ == kDisconnected);}
}void Connector::handleError()
{LOG_ERROR << "Connector::handleError state=" << state_;if (state_ == kConnecting){int sockfd = removeAndResetChannel();int err = sockets::getSocketError(sockfd);LOG_TRACE << "SO_ERROR = " << err << " " << strerror_tl(err);retry(sockfd);}
}//重连
void Connector::retry(int sockfd)
{//socket是一次性的,失败后需要关闭重新创建sockets::close(sockfd);//将状态设置为断开连接setState(kDisconnected);if (connect_){LOG_INFO << "Connector::retry - Retry connecting to " << serverAddr_.toIpPort()<< " in " << retryDelayMs_ << " milliseconds. ";//隔一段时间后重连,重新执行startInLooploop_->runAfter(retryDelayMs_/1000.0,std::bind(&Connector::startInLoop, shared_from_this()));//间隔时间翻倍retryDelayMs_ = std::min(retryDelayMs_ * 2, kMaxRetryDelayMs);}else{LOG_DEBUG << "do not connect";}
}

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

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

相关文章

Oracle中的索引碎片

索引碎片是指索引在存储空间上不连续的分布情况&#xff0c;它可能会影响到数据库性能和查询效率。索引碎片化主要由以下几个原因导致&#xff1a; 插入、更新和删除操作&#xff1a;当对表中的数据进行插入、更新或删除操作时&#xff0c;索引也需要相应地更新。这些DML操作可…

java压缩pdf体积,图片体积

pdf整体进行压缩,图片进行压缩 // 生成主证书的PDF路径 创建一个文件String pdfPath UploadDown.createFile(".pdf");outputStream new FileOutputStream(pdfPath);bufferedOutputStream new BufferedOutputStream(outputStream);writer PdfWriter.getInstance(…

操作系统 day09(线程)

线程 为什么引入线程 在没引入进程之前&#xff0c;系统中的各个程序只能串行的执行&#xff0c;比如&#xff1a;只能先听歌&#xff0c;再聊QQ。引入进程之后&#xff0c;各个程序可以并发执行&#xff0c;比如&#xff1a;一边听歌&#xff0c;一边聊QQ。但是现在QQ可以一…

springboot(ssm 企业员工薪酬关系系统 Java(codeLW)

springboot(ssm 企业员工薪酬关系系统 Java(code&LW) 开发语言&#xff1a;Java 框架&#xff1a;ssm/springboot vue JDK版本&#xff1a;JDK1.8&#xff08;或11&#xff09; 服务器&#xff1a;tomcat 数据库&#xff1a;mysql 5.7&#xff08;或8.0&#xff09; …

gitlab-ce-12.3.5 挖矿病毒及解决方案

前言 最近发现在使用gitlab提交代码的时候总是失败&#xff0c;一访问gitlab还时常报503&#xff0c;于是使用 top 命令查看了内存占用情况&#xff0c;发现了一个git进程内存使用了2.3g&#xff0c;cpu还一直占用300-400%&#xff0c; 以前不知道gitlab还有病毒&#xff0c;只…

IP 地址冲突检测工具

IP 冲突是一个术语&#xff0c;用于表示同一网络或子网中尝试使用相同 IP 地址的两个或多个设备的状态&#xff0c;这可能会导致发往特定主机的通信与其他主机混淆&#xff0c;因为两者都使用相同的 IP&#xff0c;为了避免这种情况&#xff0c;某些主机在发生 IP 冲突时会失去…

电机应用-直流有刷电机

目录 直流有刷电机 工作原理 直流有刷减速电机的重要参数 电路原理与分析 驱动芯片分析 L298N驱动芯片 直流有刷减速电机控制实现 控制速度原理 硬件设计 L298N 野火直流有刷电机驱动板-MOS管搭建板 软件设计1&#xff1a;两个直流有刷减速电机按键控制 开发设计 …

仓库自动化中的RFID技术的应用浅谈

仓库自动化与RFID技术的结合代表着现代供应链管理的一个重要革新。这两者的协同作用能够显著提升仓储效率、降低成本、增强库存管理、提高货物跟踪的准确性&#xff0c;并且使仓库操作更加智能化。 仓库自动化是一种通过应用自动化技术和系统来管理和优化仓库操作的方法。这种…

用尽可能简单易懂的代码做个时间轴(时间线)

用尽可能简单的代码&#xff0c;做个时间线或者时间轴展示功能&#xff0c;效果如图&#xff1a; 特点&#xff1a;纯DIVCSS构建&#xff0c;需要展示到什么进度&#xff0c;直接加active属性就行了。 还贴心给配了个setProgress(step)函数&#xff0c;功能太简单&#xff0c;…

gpt网站资源分享

gpt网站 gpt网站 下面是一个扫码跳转的图片&#xff1a; 里面有3.5和4模型&#xff0c;目前有免费体验&#xff0c;大家可以试试

v-model进行父子组件数据传递

v-model原理 1.原理&#xff1a; v-model本质上是一个语法糖。例如应用在输入框上&#xff0c;就是value属性 和 input事件 的合写 <template><div id"app" ><input v-model"msg" type"text"><input :value"msg&qu…

C++ 之多态(一)

什么是虚函数 在类的定义中&#xff0c;前面有 virtual 关键字的成员函数称为虚函数&#xff1b;virtual 关键字只用在类定义里的函数声明中&#xff0c;写函数体时不用。 class Base {virtual int Fun() ; // 虚函数 };int Base::Fun() // virtual 字段不用在函数体时定义 …

【MySQL】一文学会所有MySQL基础知识以及基本面试题

文章目录 前言 目录 文章目录 前言 一、主流数据库以及如何登陆数据库 二、常用命令使用 三、SQL分类 3.1 存储引擎 四、创建数据库如何设置编码等问题 4.1操纵数据库 4.2操纵表 五、数据类型 六、表的约束 七、基本查询 八、函数 九、复合查询 十、表的内连和外连 十一、索引…

Gradle国内腾讯镜像

Gradle国内镜像 腾讯 https&#xff1a;//mirrors.cloud.tencent.com/gradle/ 使用方法&#xff1a;gradle不同版本下载太慢---腾讯做了国内镜像可以直接下载_gradle 下载-CSDN博客

Luatos Air700 改变BL0942串口波特率

LuatOs 改变模块串口波特率思路参照 luatos 改变AIR530串口波特率 BL0942默认串口波特率可以通过SCLK_BPS引脚接3.3V电源设置到9600bps 但如果调整到38400bps需要修改0x19寄存器 bl0942 v1.06版的特殊寄存器说明&#xff0c;注意早期版本特殊寄存器说明存在错误 完整代码 mai…

多路转接(上)——select

目录 一、select接口 1.认识select系统调用 2.对各个参数的认识 二、编写select服务器 1.两个工具类 2.网络套接字封装 3.服务器类编写 4.源文件编写 5.运行 一、select接口 1.认识select系统调用 int select(int nfds, fd_set readfds, fd_set writefds, fd_set ex…

【mmrotate】*** is not in the task util registry

问题&#xff1a; 使用mmrotate-1.x 自定义类时&#xff0c;明明已经注册&#xff0c;并添加到__init__.py中&#xff0c;但提示没有注册 from mmdet.registry import MODELSMODELS.register_module() class RotatedATSSAssigner(BaseAssigner): 分析&#xff1a; 具体看提…

在linux安装单机版hadoop-3.3.6

一、下载hadoop https://mirrors.tuna.tsinghua.edu.cn/apache/hadoop/core/hadoop-3.3.6/ 二、配置环境变量 1、配置java环境变量 2、配置hadoop环境变量 export HADOOP_HOME/usr/local/bigdata/hadoop-3.3.6 export HBASE_HOME/usr/local/bigdata/hbase-2.5.6 export JA…

Python进行数据可视化,探索和发现数据中的模式和趋势。

文章目录 前言第一步&#xff1a;导入必要的库第二步&#xff1a;加载数据第三步&#xff1a;创建基本图表第四步&#xff1a;添加更多细节第五步&#xff1a;使用Seaborn库创建更复杂的图表关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精品Pyth…

Vue 将响应式数据转为普通对象

toRaw&#xff1a;将一个 reactive 生成的响应式数据转为普通对象。 toRaw 适用于&#xff1a;获取响应式数据对应的普通对象&#xff0c;对这个普通对象所有的操作&#xff0c;都不会引起页面的更新。 markRaw&#xff1a;标记一个对象&#xff0c;使其永远不会再成为响应式…