分析源码学习c++(srs中http客户端)

文章目录

  • 背景
  • 基础知识
    • c++标准库
    • 虚函数
      • 虚函数使用方法
    • 虚析构函数
  • HTTP客户端
    • 使用方法
    • TCP传输层分析
      • 使用方法
      • 结构分析
      • 连接函数
      • 读写函数
    • 协议层分析
      • 初始化函数
      • 发送请求
      • 响应数据解析

背景

通过阅读源码,编写分析笔记来学习C++是一种非常有效且深入的方法,能帮助理解C++语言的底层机制、编程范式、设计模式以及常用实践。

基础知识

c++标准库

C++标准模板库(STL,Standard Template Library)是C++标准库的一个重要组成部分,提供了一套功能强大的模板类和函数,用于实现各种常用的数据结构和算法

在 C++ 中,使用标准库容器(如 std::vector)时,不需要手动释放内存 ,标准库容器内部已经实现了智能内存管理机制。比如创建一个
std::vector 对象时,它会自动管理分配的内存,并在对象不再使用时自动释放内存。

常用STL包括

  • 容器
    vector(向量)、list(链表)、deque(双端队列)、queue(队列)、stack(栈)、set(集合)、map(映射表)
  • 算法
    排序、查找、替换等操作
  • 迭代器
    封装了用于遍历容器元素的指针的类模板
  • 适配器
    容器适配器是对其他容器的封装,提供特定的接口以适配特定的使用场景。常见的适配器包括stack、queue和priority_queue等,它们通过封装其他容器(如deque或list)来实现栈、队列和优先队列等功能

编译命令

g++ -std=c++11 test-std.cpp -o test

输出
在这里插入图片描述

测试代码

#include <iostream>
#include <vector>
#include <map> 
#include <string>
#include <set> 
int main() {// 创建一个空的整数向量std::vector<int> myVector;// 添加元素到向量中myVector.push_back(9);myVector.push_back(7);myVector.push_back(2);myVector.push_back(5);// 访问向量中的元素并输出std::cout << "Elements in the vector: ";for (int element : myVector) {std::cout << element << " ";}std::cout << std::endl;// 访问向量中的第一个元素并输出std::cout << "First element: " << myVector[0] << std::endl;// 访问向量中的第二个元素并输出std::cout << "Second element: " << myVector.at(1) << std::endl;// 获取向量的大小并输出std::cout << "Size of the vector: " << myVector.size() << std::endl;// 删除向量中的第三个元素myVector.erase(myVector.begin() + 2);// 输出删除元素后的向量std::cout << "Elements in the vector after erasing: ";for (int element : myVector) {std::cout << element << " ";}std::cout << std::endl;// 清空向量并输出myVector.clear();std::cout << "Size of the vector after clearing: " << myVector.size() << std::endl;// 创建一个map,键为string类型,值为int类型  std::map<std::string, int> ageMap;  // 向map中插入元素   ageMap["zhao"] = 30;  ageMap["qian"] = 25;  ageMap["sun"] = 35;// 遍历输出for (const auto& pair : ageMap) {  std::cout << pair.first << ": " << pair.second << std::endl;  }   // 查找并修改map中的元素  if (ageMap.find("sun") != ageMap.end()) {  ageMap["sun"] = 26; // Bob的年龄增加1岁}std::cout << "sun age: " << ageMap["sun"] << std::endl;std::cout << "li age: " << ageMap["li"] << std::endl;std::set<std::string> names; names.insert("li");  names.insert("zhou");names.insert("wu");names.insert("wu");for (const auto& name : names) {  std::cout << name << std::endl;  }  names.erase("zhou"); if (names.find("zhou") != names.end()) {  std::cout << "Found zhou in the set!" << std::endl;  }else {std::cout << "NOT Found zhou in the set!" << std::endl;}  return 0;
}

虚函数

子类可以重写(Override)父类的方法,为什么还需要虚函数?
需要理解静态绑定(在编译时确定调用哪个函数)和动态绑定(运行时)
如果你只是简单地重写一个基类方法,但没有将该方法声明为虚函数,那么通过基类指针或引用调用该方法时,将只会调用基类的版本(静态绑定)。这意味着即使你有一个派生类对象,并且该对象重写了该方法,通过基类指针或引用调用时也不会调用到派生类的版本

  • 虚函数允许在派生类中重写基类中的函数,并且在运行时根据对象的实际类型来决定调用哪个函数版本
  • 当类里面有虚函数的时候,编译器会给类添加一个虚函数表,里面来存放虚函数指针,这样就会增加类的存储空间

虚函数使用方法

在基类中声明虚函数时,需要使用关键字 virtual。

class Base {
public:virtual void display() {std::cout << "Base display" << std::endl;}
};

派生类重写虚函数 virtual 关键字 可加可不加

class Derived : public Base {
public:void display() override {std::cout << "Derived display" << std::endl;}
};

通过基类指针或引用调用虚函数

void callDisplay(Base* base) {base->display();  // 调用虚函数
}
int main() {Base* basePtr = new Derived();callDisplay(basePtr);  // 调用 Derived::display()Base& baseRef = *basePtr;baseRef.display();  // 调用 Derived::display()delete basePtr;  // 释放内存return 0;
}

虚析构函数

虚析构函数确保在通过基类指针删除派生类对象时,能够正确调用派生类的析构函数
虚析构函数是为了避免内存泄露,当子类中会有指针成员变量时,通过父类指针操作。很容易发生内存泄漏。此时虚析构函数使得在删除指向子类对象的基类指针时可以调用子类的析构函数达到释放子类中堆内存的目的,而防止内存泄露的。

#include <iostream>
#include <string>
class Base {
public:virtual ~Base() {} ; // 虚析构函数virtual   void  DoSomething() { std::cout  <<   " 这里是基类! "   <<   std::endl; };
};class Derived : public Base {
public:~Derived() {std::cout << "派生类 destructor" << std::endl;}void  DoSomething() {  std::cout  <<   " 这里是派生类! "   <<   std::endl; };
};int main() {Base* basePtr = new Derived();basePtr->DoSomething();delete basePtr;  // 调用 Derived 的析构函数return 0;
}

输出

 这里是派生类! 
派生类 destructor

如果把类Base 析构函数前的virtual去掉,那输出结果就是下面

这里是派生类! 

派生类的析构函数没有被调用

HTTP客户端

以srs中的http客户端为例进行分析

使用方法

   SrsHttpClient hc;hc.initialize("127.0.0.1", 80, 9000);hc.get("/api/v1/version", "Hello world!", msg);
  • 使用例子
SrsHttpClient client;
client.initialize("http", "127.0.0.1", 8080, 1000000LL)
ISrsHttpMessage* res = NULL;
SrsAutoFree(ISrsHttpMessage, res);
client.get("/api/v1", "", &res) // 发送请求获取数据 
ISrsHttpResponseReader* br = res->body_reader(); // 读取body中内容ssize_t nn = 0; char buf[1024];br->read(buf, sizeof(buf), &nn)//数据存入buf

其中 SrsHttpClient 提供 initialize初始化 get post set_header设置头 、设置读超时 等常用方法 ,还提供kbps_sample统计方法

class SrsHttpClient{std::string schema_; // 协议类型 http 或者https std::string host;   //服务器 ip 域名int port;           //服务器端口SrsTcpClient* transport; // 提供 连接 读写方法 SrsHttpParser* parser;// http协议解析 std::map<std::string, std::string> headers;SrsNetworkKbps* kbps;
}

TCP传输层分析

SrsTcpClient 分析

使用方法

SrsTcpClient client("127.0.0.1", 1935, 9 * SRS_UTIME_SECONDS);
client.connect();
client.write("Hello world!", 12, NULL);
client.read(buf, 4096, NULL);

结构分析

class SrsTcpClient : public ISrsProtocolReadWriter // 读写接口ISrsProtocolReadWriter 中的 读接口ISrsProtocolReader(封装 IReader+统计 IStatistic)

构造函数

 SrsTcpClient::SrsTcpClient(string h, int p, srs_utime_t tm)
{stfd_ = NULL;io = new SrsStSocket();    //初始化io为NULLhost = h;port = p;timeout = tm;
}

连接函数

SrsTcpClient::connect()

srs_netfd_t stfd = NULL;// srs_netfd_t  == st_netfd_t 
srs_tcp_connect(host, port, timeout, &stfd) //调用st_connect  { 流程 1 设置addrinfo  2 getaddrinfo 解析域名 3 生成socket 4 socket转 stfd  5 st_connect}
io = new SrsStSocket(stfd); //赋值 
stfd_ = stfd;  //赋值 

SrsStSocket 封装 stfd_ 超时时间 读写字节数 read write方法

读写函数

调用的是 SrsStSocket::read(void* buf, size_t size, ssize_t* nread)
函数中根据是否设置超时时间进行不同读取

nb_read = st_read((st_netfd_t)stfd_, buf, size, ST_UTIME_NO_TIMEOUT);
或者
nb_read = st_read((st_netfd_t)stfd_, buf, size, rtm);rbytes += nb_read;

错误处理

 if (nb_read <= 0) {if (nb_read < 0 && errno == ETIME) {// 超时没有读到数据 return srs_error_new(ERROR_SOCKET_TIMEOUT, "timeout %d ms", srsu2msi(rtm));}        if (nb_read == 0) { // 连接异常 或者读取结束errno = ECONNRESET;}return srs_error_new(ERROR_SOCKET_READ, "read");}

写类似 调用
SrsStSocket::write(void* buf, size_t size, ssize_t* nwrite)方法
最终调用st库的

 st_write((st_netfd_t)stfd_, buf, size, stm);

除了支持 write 还有writev方法 iovec结构

协议层分析

初始化函数

srs_error_t SrsHttpClient::initialize(string schema, string h, int p, srs_utime_t tm)
{parser = new SrsHttpParser();parser->initialize(HTTP_RESPONSE)//设置默认头 host uaheaders["Host"] = ep;……
}

发送请求

返回结果存放在ppmsg 中

srs_error_t SrsHttpClient::get(string path, string req, ISrsHttpMessage** ppmsg) 
{设置请求头长度 req的长度调用 SrsHttpClient::connect(){ 	1 transport = new SrsTcpClient   2  transport->connect() 3  kbps->set_io(transport, transport);}按照请求协议拼接字符串GET %s HTTP/1.1\r\nHost: %s\r\nContent-Length: %d\r\n\r\n%s//调用 SrsHttpClient::writer() 发送  writer 判断是http 还是https 链路writer()->write((void*)data.c_str(), data.length(), NULL)ISrsHttpMessage* msg = NULL;parser->parse_message(reader(), &msg)*ppmsg = msg;return 
}

post 函数类似

POST %s HTTP/1.1\r\nHost: %s\r\nContent-Length: %d\r\n\r\n%s

响应数据解析

SrsHttpParser::parse_message(ISrsReader* reader, ISrsHttpMessage** ppmsg)
{state = SrsHttpParseStateInit;//  初始化状态 http_parser_init(&parser, type_); parse_message_imp(reader)SrsHttpMessage* msg = new SrsHttpMessage(reader, buffer);返回msg
}
parse_message_imp
srs_error_t SrsHttpParser::parse_message_imp(ISrsReader* reader)
{ 循环从reader中读取数据到buffer  直到全部读取完成http_parser_execute(&parser, &settings, buffer->bytes(), buffer->size());buffer->read_slice(consumed);if (state >= SrsHttpParseStateHeaderComplete) { // 判断状态break;}
}

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

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

相关文章

论文解读 | ACL2024 Outstanding Paper:因果指导的主动学习方法:助力大语言模型自动识别并去除偏见...

点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入&#xff01; 点击阅读原文观看作者直播讲解回放&#xff01; 作者简介 孙洲浩&#xff0c;哈尔滨工业大学SCIR实验室博士生 概述 尽管大语言模型&#xff08;LLMs&#xff09;展现出了非常强大的能力&#xff0c;但它们仍然…

数据源10min自动断开连接导致查询抛异常(未获取可用连接)

由于个人能力有限&#xff0c;本文章仅仅代表本人想法&#xff0c;若有不对请即时指出&#xff0c;若有侵权&#xff0c;请联系本人。 1 背景 工作中引入druid来管理数据源连接&#xff0c;由于数据源每隔10分钟强制管理空闲超过10分钟的连接&#xff0c;导致每隔10分钟出现1…

Web攻防之应急响应(二)

目录 前提 &#x1f354;学习Java内存马前置知识 内存马 内存马的介绍 内存马的类型众多 内存马的存在形式 Java web的基础知识&#xff1a; Java内存马的排查思路&#xff1a; &#x1f354;开始查杀之前的需要准备 1.登录主机启动服务器 2.生成jsp马并连接成功 …

【Linux】多线程:线程概念,线程与进程的区别与联系,多线程相较于多进程的优势

目录 一、进程基本属性回顾 二、线程概念 三、操作系统为什么要引入线程—多进程和多线程的区别 为什么多线程比多线程调度效率更快&#xff1f; 四、线程的优点 五、线程的缺点 六、线程异常 一、进程基本属性回顾 在学习线程之前&#xff0c;我们先来回顾一下进程的基…

注册安全分析报告:熊猫频道

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞…

【Rust】007-包管理与模块管理

【Rust】007-包管理与模块管理 文章目录 【Rust】007-包管理与模块管理一、包管理器&#xff1a;Cargo1、简介Cargo 官方文档仓库 2、项目初始化3、写一个小程序任务目标寻找合适的库添加库到我们的项目中代码实现cargo run运行 二、模块管理1、概述2、文件作为模块第一步&…

前端模拟面试:如何检查JavaScript对象属性是否存在?

你正在参加一场关键的前端开发面试&#xff0c;面试官提出了一个经典的JavaScript问题&#xff1a;“在JavaScript中&#xff0c;如何检查对象是否包含某个属性&#xff1f;请你详细介绍几种不同的方法&#xff0c;并解释它们的区别。” 这个问题不仅考验你对JavaScript的基础掌…

怎样在公司将手机屏幕(远程)投屏到家里的大电视上?

我不住家里&#xff0c;前几次回去都会替老爸老妈清理手机。这两个星期没空回去&#xff0c;老爸吐槽手机用几天就又卡了&#xff0c;其实就是清理一些手机缓存的问题。 我说我远程控制他的手机&#xff0c;给他清理一下。他一听“控制”就不喜欢&#xff0c;说我大了&#xf…

sM4040B科学级显微制冷相机特性

sM4040B科学级显微制冷相机特性 sM4040B搭载了 GSENSE4040BSI 3.2 英寸图像传感器&#xff0c;针对传感器固有的热噪声&#xff0c;专门设计了高效制冷模块&#xff0c;使得相机传感器的工作温度比环境温度低达 35-40 度。针对制冷相机常见的低温结雾现象设计了防结雾机制&…

【Python百日进阶-Web开发-音频】Day707 - 时域处理 librosa.autocorrelate

文章目录 一、时域处理1.1 librosa.autocorrelate1.1.1 语法与参数1.1.2 例子1.1.2.1 计算完全自相关y1.1.2.2 计算长达 4 秒的起始强度自相关 一、时域处理 1.1 librosa.autocorrelate https://librosa.org/doc/latest/generated/librosa.autocorrelate.html 1.1.1 语法与参…

哪款宠物空气净化器能更好的清理浮毛?希喂、352、IAM测评分享

家里这三只可爱的小猫咪&#xff0c;已然成为了我们生活中不可或缺的家庭成员&#xff0c;陪伴我们度过了说长不长说短不短的五年时光。时常庆幸自己当年选择养它们&#xff0c;在我失落的时候总能给我安慰&#xff0c;治愈我多时。 但这个温馨的背后也有一点小烦恼&#xff0…

记一种常用的实时数据同步方案:Canal+Kafka+Flume

记一种常用的实时数据同步方案&#xff1a;CanalKafkaFlume 在当今数据驱动的业务环境中&#xff0c;数据同步是确保系统间数据一致性的关键环节。一种高效、稳定且可扩展的数据同步方案对于支撑企业的数据处理和分析需求至关重要。本文将介绍一种结合了Canal、Kafka和Flume的…

IOS 20 发现界面(UITableView)歌单列表(UICollectionView)实现

发现界面完整效果 本文实现歌单列表效果 文章基于 IOS 19 发现界面&#xff08;UITableView&#xff09;快捷按钮实现 继续实现发现界面歌单列表效果 歌单列表Cell实现 实现流程&#xff1a; 1.创建Cell&#xff0c;及在使用UITableView的Controller控制器上注册Cell&#x…

STM32F103C8----GPIO(跟着江科大学STM32)

一&#xff0c;GPIO简介 GPIO&#xff08;General Purpose Input Output&#xff09;通用输入输出口 可配置为8种输入输出模式 引脚电平&#xff1a;0V~3.3V&#xff08;0V&#xff09;&#xff0c;部分引脚可容忍5V 输出模式下可控制端口输出高低电平&#xff0c;用以驱动…

AI-Talk开发板之LED

一、说明 AI-Talk开发板上有一颗用户LED&#xff0c;连接在CH32 PA2管脚&#xff0c;低电平亮&#xff0c;高电平灭。 相关电路图如下&#xff1a; 需要提前给CH32V003烧录特定的固件才能将CH32作为CSK6011A的exmcu&#xff0c;参考AI-Talk开发板更新CH32固件。​​​​​​​…

如何查看Mac的处理器架构‌‌是ARM还是x86

‌通过命令行查看Mac的处理器架构‌‌ 打开终端&#xff08;Terminal&#xff09;。输入命令 uname -m 并回车。如果输出结果是 arm64&#xff0c;则表示你的Mac使用的是ARM架构&#xff1b;如果输出结果是 x86_64&#xff0c;则表示你的Mac使用的是x86架构。 如图&#xff1…

2024/9/4黑马头条跟学笔记(二)

app端文章列表 学习内容 需求分析 上方分类频道切换 布局&#xff0c;无图&#xff0c;单图&#xff0c;三张图 文章数据库表 导入文章数据库 结构分析 配置-文章 一对一&#xff0c;拆表&#xff0c;冷热数据分离满足范式 表的拆分-垂直分表 优势 查文章信息不会连带查…

Day10_0.1基础学习MATLAB学习小技巧总结(10)——程序流程控制

利用空闲时间把碎片化的MATLAB知识重新系统的学习一遍&#xff0c;为了在这个过程中加深印象&#xff0c;也为了能够有所足迹&#xff0c;我会把自己的学习总结发在专栏中&#xff0c;以便学习交流。 素材来源“数学建模清风” 特此说明&#xff1a;本博客的内容只在于总结在…

页面小组件-搜索栏(一)

样例展示 效果示例-折叠状态 效果示例-展开状态 代码示例 <custom-search-wrapper><!--showFoldBtn 需要展示折叠按钮时传值--><template slotleft><el-form:model"searchFormData"inlinesize"small"><el-form-item><e…

前端入门了解

1. 网页 1.1 网页概述 1.2 超文本标记语言 1.3 网页的形成 2. 浏览器了解 网页需要通过浏览器来展示&#xff0c;下面是关于浏览器的两点; 国际上通用的浏览器有如下六个&#xff08;百度&#xff0c;360&#xff0c;uc等是主要在国内使用&#xff09;&#xff0c; 3. We…