C/C++精品项目之图床共享云存储(3):网络缓冲区类和main

一:网络缓冲区

下面是我写过关于网络缓冲区的两篇文章,一篇理论,一篇实战。这两篇的讲解都比本项目使用的难。

Linux下的网络缓冲区设计(分模块讲解)-CSDN博客

Linux下网络缓冲区——chainbuffer的具体设计-CSDN博客

         我们在进行网络传输消息的时候,我们一般会直接使用系统的网络缓冲区,但是不利于我们进行管理和控制,于是我们直接手写网络缓冲区。我们看下面的类:其中包含存放数据的地址,和分配的大小,以及占用的地址或者说是占用的大小。这个类比较简单,我们很容易能看出来他们的职责。

        对于他的构造函数和析构函数,都是比较简单也就是直接将数据进行初始化和删除数据的操作。对于下面的四个函数都是字面意思,返回一些需要的数据。

class DLL_MODIFIER CSimpleBuffer {public:CSimpleBuffer();~CSimpleBuffer();uchar_t *GetBuffer() { return buf_; }uint32_t GetAllocSize() { return alloc_size_; }uint32_t GetWriteOffset() { return write_offset_; }void IncWriteOffset(uint32_t len) { write_offset_ += len; }void Extend(uint32_t len);uint32_t Write(void *buf, uint32_t len);uint32_t Read(void *buf, uint32_t len);private:uchar_t *buf_;                //开辟空间uint32_t alloc_size_;         //分配大小uint32_t write_offset_;       //写的偏移量
};

        对于这个扩展函数来说,我们一开始就开辟一块内存,但是当我们继续来数据的时候,我们这个开辟的空间不够用,于是我们需要在这个基础上进行扩展内存。在扩展的时候我们要多开辟一点内存,防止内存溢出。

void CSimpleBuffer::Extend(uint32_t len) {      //扩展缓冲区alloc_size_ = write_offset_ + len;          //在这里我们是使用之前使用的和加上新开辟的,组成新的缓冲区。alloc_size_ += alloc_size_ >> 2; // increase by 1/4 allocate size           //每次还额外增加四分之一的空间。uchar_t *new_buf = (uchar_t *)realloc(buf_, alloc_size_);    //在原先的内存块上扩展内存,如果无法扩展,\则分配个新的内存块,并将数据复制到新的内存块中。buf_ = new_buf;
}

        我们在写数据的时候,肯定要先判断我们的空间是否够,如果不够那么就扩展呗。对于读取数据比较简单。

uint32_t CSimpleBuffer::Write(void *buf, uint32_t len) {        //每次写之前都要看看空间够不够,不够的话就扩展。if (write_offset_ + len > alloc_size_) {Extend(len);}if (buf) {memcpy(buf_ + write_offset_, buf, len);        //将buf_ + write_offset_ 位置开始,将buf中的数据拷贝到buf_ + write_offset_位置开始,拷贝len个字节。}write_offset_ += len;return len;
}uint32_t CSimpleBuffer::Read(void *buf, uint32_t len) {     //将数据读出来。if (0 == len)return len;if (len > write_offset_)len = write_offset_;if (buf)memcpy(buf, buf_, len);write_offset_ -= len;memmove(buf_, buf_ + len, write_offset_);return len;
}

二:main函数,将之前写的一些基础组件进行组合

        在main函数中我们一共有八个部分,第一二部分就是我们要加载的数据库配置文件以及要监听的端口和地址。我们先加载它,我们这个加载的函数并不是自己写的,使用别人写好的,会使用就可以了。三四就是数据库开始初始化,五六就是网络模块的初始化,七八就是开始循环等待客户端的连接。

void http_callback(void *callback_data, uint8_t msg, uint32_t socket_handle,void *pParam) {UNUSED(callback_data);UNUSED(pParam);if (msg == NETLIB_MSG_CONNECT) {// 这里是不是觉得很奇怪,为什么new了对象却没有释放?// 实际上对象在被Close时使用delete this的方式释放自己CHttpConn *pConn = new CHttpConn();pConn->OnConnect(socket_handle);} else {LogError("!!!error msg:{}", msg);}
}int main(int argc, char *argv[]) {// 默认情况下,往一个读端关闭的管道或socket连接中写数据将引发SIGPIPE信号。我们需要在代码中捕获并处理该信号,// 或者至少忽略它,因为程序接收到SIGPIPE信号的默认行为是结束进程,而我们绝对不希望因为错误的写操作而导致程序退出。// SIG_IGN 忽略信号的处理程序signal(SIGPIPE, SIG_IGN); //忽略SIGPIPE信号int ret = 0;// 1.获取配置文件路径//配置文件中就是服务端监听的端口,比如本地和8081,还有加载的数据库的配置信息。char *str_tc_http_server_conf = NULL;if(argc > 1) {str_tc_http_server_conf = argv[1];  // 指向配置文件路径} else {str_tc_http_server_conf = (char *)"tc_http_server.conf";}printf("conf file path: %s\n", str_tc_http_server_conf);// 2.读取配置文件//2.1 解析配置文件CConfigFileReader config_file;     //读取配置文件       开源文件if(config_file.ParseConf(str_tc_http_server_conf) != 0) {std::cout << str_tc_http_server_conf << " no exist, please check conf file\n";return -1;}// 2.2读取配置文件字段// 日志级别char *log_level = config_file.GetConfigName("log_level");   //读取日志设置级别if (!log_level) {LogError("config item missing, exit... log_level:{}", log_level);return -1;}DLog::SetLevel(log_level);   //设置日志打印级别     spdlog// http监听地址和端口char *http_listen_ip = config_file.GetConfigName("HttpListenIP");char *str_http_port = config_file.GetConfigName("HttpPort");        //8081 -- nginx.conf,当前服务的端口// 检测监听ip和端口是否存在if (!http_listen_ip || !str_http_port) {LogError("config item missing, exit... ip:{}, port:{}", http_listen_ip, str_http_port);return -1;}uint16_t http_port = atoi(str_http_port);// 打印提示而已LogInfo("main into"); //单例模式 日志库 spdlog          我们这里已经define了,在log里面,这里可以直接使用//3.初始化 redis 连接池,内部也会读取读取配置文件tc_http_server.conf  CacheManager::SetConfPath(str_tc_http_server_conf); //设置配置文件路径CacheManager *cache_manager = CacheManager::getInstance();if (!cache_manager) {LogError("CacheManager init failed");return -1;}//4.初始化 mysql 连接池,内部也会读取读取配置文件tc_http_server.confCDBManager::SetConfPath(str_tc_http_server_conf);   //设置配置文件路径CDBManager *db_manager = CDBManager::getInstance();if (!db_manager) {LogError("DBManager init failed");return -1;}//5.reactor网络模型, 这里只对Windows系统有作用,Linux环境调用这个函数实际没有做什么ret = netlib_init();if (ret == NETLIB_ERROR) {LogError("netlib_init failed");return ret;}//6.监听指定的IP和端口,并绑定accept新连接后的回调函数http_callback, 这里主要是我们http端口ret = netlib_listen(http_listen_ip, http_port, http_callback, NULL);        //就是普通的网络监听//创建socket,服务端接听,然后设置客户端的cb,然后onwrite,执行callback。if (ret == NETLIB_ERROR) {LogError("listen {}:{} failed", http_listen_ip, http_port);return ret;}LogInfo("server start listen on:For http://{}:{}", http_listen_ip, http_port);LogInfo("now enter the event loop...");//7.将当前进程id写入文件server.pid, 可以直接cat这个文件获取进程idWritePid();//8.进入epoll_wait触发的循环流程  netlib_eventloop(1);return 0;
}

我们重点讲一下,这个回调函数:http_callback。

        我们看一下这个回调函数是在哪里传入进去的,他是在第六个位置,我们的网络在监听的时候,当客户端连接上来给他配置的回调函数。我们在回想一下这个回调函数会在哪里被执行?由于他是客户端自带的一个回调函数,那么我们的回调函数被执行就是在触发写事件的时候。

        那么在这个回调函数中我们看到创建了HTTP的conn。那我们来看看这个类都写了什么。因为我们这里先看整体的main函数,因此先只看onconnect函数,这个函数到底做了什么。

class CHttpConn : public CRefObject {
public://构造函数CHttpConn();//析构函数virtual ~CHttpConn();// 获取当前自定义的唯一handleuint32_t GetConnHandle() { return conn_handle_; }// 返回对端ipchar *GetPeerIP() { return (char *)peer_ip_.c_str(); }//发送数据,最终调用socket的sendint Send(void *data, int len);//关闭连接,最终调用socket的closevoid Close();// accept收到新连接fd,触发OnConnect的调用,并把socket fd传递进来      //在接到新连接之后,执行回调函数,会传入socket的fd。不信自己去accept去看。void OnConnect(net_handle_t socket_handle);// 可读事件触发回调OnReadvoid OnRead();// 可写事件触发回调OnWritevoid OnWrite();// 关闭事件触发OnClosevoid OnClose();// 数据发送完毕回调OnWriteComlete,自己业务处理的void OnWriteComlete();private:      // 账号注册处理void _HandleRegisterRequest(string &url, string &post_data);// 账号登陆处理void _HandleLoginRequest(string &url, string &post_data);//获取我的文件列表void _HandleMyfilesRequest(string &url, string &post_data);protected://socket fdnet_handle_t socket_handle_;  // 业务自定义唯一标识uint32_t conn_handle_;//当前socket写缓存是否有空间,=ture当前socket写缓存已满,=false当前socket写缓存有空间可以写bool busy_;//连接状态uint32_t state_;//保存对端ipstd::string peer_ip_;//保存对端端口uint16_t peer_port_;//缓存从socket读取的数据CSimpleBuffer in_buf_;//缓存还没有发送出去的数据CSimpleBuffer out_buf_;// http解析CHttpParserWrapper http_parser_;
};

        首先我们写类的函数的时候,我们先看一下其他重要的函数,因为这个类是一个HTTP的连接,所以和basesocket一样都需要一个寻找这个类的函数,以及存放整体的一个容器。然后我们在这个http连接中在传入一个callback,用于使用这个http的socket全部信息。

CHttpConn *FindHttpConnByHandle(uint32_t handle);
typedef hash_map<uint32_t, CHttpConn *> HttpConnMap_t;
// conn_handle 从0开始递增,可以防止因socket handle重用引起的一些冲突
static uint32_t g_conn_handle_generator = 0;static HttpConnMap_t g_http_conn_map;CHttpConn *FindHttpConnByHandle(uint32_t conn_handle) {CHttpConn *pConn = NULL;HttpConnMap_t::iterator it = g_http_conn_map.find(conn_handle);if (it != g_http_conn_map.end()) {pConn = it->second;}return pConn;
}// 连接的处理函数 
void httpconn_callback(void *callback_data, uint8_t msg, uint32_t handle,uint32_t uParam, void *pParam) {NOTUSED_ARG(uParam);NOTUSED_ARG(pParam);// convert void* to uint32_t, oopsuint32_t conn_handle = *((uint32_t *)(&callback_data)); //获取连接标识CHttpConn *pConn = FindHttpConnByHandle(conn_handle);   //根据连接标识找到对应的http对象if (!pConn) {   //如果没有则返回// LogWarn("no find conn_handle: {}", conn_handle);return;}pConn->AddRef();        //添加引用计数switch (msg) {case NETLIB_MSG_READ:   //可读事件pConn->OnRead();break;case NETLIB_MSG_WRITE:  //可写事件pConn->OnWrite();break;case NETLIB_MSG_CLOSE:pConn->OnClose();   //关闭事件break;default:LogError("!!!httpconn_callback error msg:{}", msg); //fix me,可以考虑如果这里触发,直接关闭该连接// pConn->Close();break;}pConn->ReleaseRef(); //释放引用计数,如果数据发送完毕,对应的http 连接在这个位置为析构对象
}

也就是说,我们在与服务端进行连接的时候,我们在这个连接中又创建了一个http的类,在这个连接中,使用这个类进行通信。

到目前位置,我们只讲解了一些基础的组件,以及将这些组件组合起来了,这样我们就可以正常的接收外部的连接了。我们还没有讲一些业务相关的代码,会在以后进行讲解。0voice · GitHub

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

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

相关文章

区块链技术在慈善捐赠中的应用

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 区块链技术在慈善捐赠中的应用 区块链技术在慈善捐赠中的应用 区块链技术在慈善捐赠中的应用 引言 区块链技术概述 定义与原理 发…

mongoDB的安装及使用

mongodb的安装参考&#xff1a; Centos系统中mongodb的安装详解_centos安装mongodb-CSDN博客 不要下载最新的版本&#xff0c;新的版本中mongo命令无法使用&#xff0c;也就是安装后不能通过mongo命令登录&#xff0c;我这里使用5.0.30版本&#xff1b; mongodb客户端demo: …

DNS面临的4大类共计11小类安全风险及防御措施

DNS在设计之初&#xff0c;并未考虑网络安全限制&#xff0c;导致了许多问题。DNS安全扩展(DNSSEC)协议的开发旨在解决DNS的安全漏洞&#xff0c;但其部署并不广泛&#xff0c;DNS仍面临各种攻击。接下来我们一起看下DNS都存在哪些安全攻击及缓解措施&#xff0c;旨在对DNS安全…

MySql结合element-plus pagination的分页查询

实现效果如下&#xff1a; 重点&#xff1a;使用mysql查询的limit和offset 原生SQL写法&#xff1a; select c.id as deptid,c.name as department,position,a.name staffname,2024-11 as shijian ,CASE WHEN b.shijian IS NULL THEN no ELSE yes END AS submit from fa_wecom…

ubuntu20.04安装FLIR灰点相机BFS-PGE-16S2C-CS的ROS驱动

一、Spinnaker 安装 1.1Spinnaker 下载 下载地址为&#xff1a; https://www.teledynevisionsolutions.com/support/support-center/software-firmware-downloads/iis/spinnaker-sdk-download/spinnaker-sdk–download-files/?pnSpinnakerSDK&vnSpinnakerSDK 在上述地址中…

什么是数字图像?

点赞 关注 收藏 学会了 什么是数字图像&#xff1f; 本文可在公众号「德育处主任」免费阅读 弄懂数字图像的概念对学习计算机视觉很有帮助。 那么&#xff0c;什么是数字图像&#xff1f; 字面意思&#xff0c;数字图像就是有数字组成图像。通常由像素&#xff08;Pixel&…

2024年11月13日

1.创业法律指南 留置权和其他三个权 定金和订金 一般保证和连带保证 1.案例 物权编之担保法律制度案例一 冯系养鸡专业户&#xff0c;为改建鸡会和引进良种需资金20万元。冯向陈借款10万元&#xff0c;以自己的一套价值10万元的音响设备抵押&#xff0c;双方立有抵押字据&a…

Android OpenGL ES详解——立方体贴图

目录 一、概念 二、如何使用 1、创建立方体贴图 2、生成纹理 3、设置纹理环绕和过滤方式 4、激活和绑定立方体贴图 三、应用举例——天空盒 1、概念 2、加载天空盒 3、显示天空盒 4、优化 四、应用举例——环境映射:反射 五、应用举例——环境映射:折射 六、应用…

2024版本IDEA创建Sprintboot项目下载依赖缓慢

目录 步骤一&#xff1a;在IDEA中搜索Maven(双击shift) 步骤二&#xff1a;找到Maven下的settings.xml文件修改镜像 ​编辑 ​编辑​编辑 步骤三&#xff1a;用VScode打开settings.xml文件修改镜像 ​编辑 步骤一&#xff1a;在IDEA中搜索Maven(双击shift) 步骤二&#xff…

Android Framework AMS(16)进程管理

该系列文章总纲链接&#xff1a;专题总纲目录 Android Framework 总纲 本章关键点总结 & 说明&#xff1a; 说明&#xff1a;本章节主要解读AMS 进程方面的知识。关注思维导图中左上侧部分即可。 我们本章节主要是对Android进程管理相关知识有一个基本的了解。先来了解下L…

python购物计算 2024年6月青少年电子学会等级考试 中小学生python编程等级考试一级真题答案解析

目录 python购物计算 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序代码 四、程序说明 五、运行结果 六、考点分析 七、 推荐资料 1、蓝桥杯比赛 2、考级资料 3、其它资料 python购物计算 2024年6月 python编程等级考试一级编程题 一、题目要求 …

Pycharm PyQt5 环境搭建创建第一个Hello程序

第一步: 创建Pycharm项目,下载包: pip install PyQt5 -i https://pypi.tuna.tsinghua.edu.cn/simple/pip install PyQt5-tools -i https://pypi.tuna.tsinghua.edu.cn/simple/下载好了之后,可以看到相应包: PyQt5:PyQt5是一套Python绑定Digia QT5应用的框架。Qt库是最…

【Vue】Vue3.0(十九)Vue 3.0 中一种组件间通信方式-自定义事件

文章目录 一、自定义事件概念及使用场景二、代码解释三、新的示例 一、自定义事件概念及使用场景 概念 在 Vue 3.0 中&#xff0c;自定义事件是一种组件间通信的机制&#xff0c;允许子组件向父组件传递数据或触发父组件中的操作。子组件通过defineEmits函数定义可以触发的事件…

Java的dto,和多表的调用

1理论 需求是新增菜品eg&#xff1a;菜名:豆腐脑&#xff1b;口味&#xff1a;甜口&#xff0c;咸口&#xff0c; 菜单表&#xff1a;dish&#xff1b;口味表dish_flavor&#xff1b; 1dto:数据传输对象 新建一个dishDto对象有两个表里的属性 2用到两个表&#xff0c;dish,d…

【前端学习指南】Vue computed 计算属性 watch 监听器

&#x1f36d; Hello&#xff0c;我是爱吃糖的范同学 &#x1f534; 想把自己学习技术的经历和一些总结分享给大家&#xff01; &#x1f534; 通过这样的方式记录自己成长&#xff0c;同时沉淀自己的技术&#xff0c;我会把所有额外的时间和经历投放到CSDN和公众号&#xff0…

【算法】——二分查找合集

阿华代码&#xff0c;不是逆风&#xff0c;就是我疯 你们的点赞收藏是我前进最大的动力&#xff01;&#xff01; 希望本文内容能够帮助到你&#xff01;&#xff01; 目录 零&#xff1a;二分查找工具 1&#xff1a;最基础模版 2&#xff1a;mid落点问题 一&#xff1a;最…

读数据质量管理:数据可靠性与数据质量问题解决之道03数据目录

1. 同步数据 1.1. 不同的数据仓库和数据湖通过数据集成层来进行桥接 1.2. AWS Glue、Fivetran和Matillion等数据集成工具从不同来源收集数据&#xff0c;统一这些数据&#xff0c;并将其转换为上游来源 1.3. 数据集成的一个典型用例是收集数据湖的数据并以结构化格式将其加载…

openSUSE 环境下通过 zypper 安装软件

操作场景 为了提升您在云服务器上的软件安装效率&#xff0c;减少下载和安装软件的成本&#xff0c;腾讯云提供了 zypper 下载源。openSUSE 操作系统和部分 SLES 的云服务器用户可通过 zypper 快速安装软件。本文档以 openSUSE 操作系统为例&#xff0c;指导您通过 zypper 快速…

ima.copilot-腾讯智能工作台

一、产品描述 ima.copilot是腾讯推出的基于腾讯混元大模型技术的智能工作台&#xff0c;通过先进的人工智能技术&#xff0c;为用户提供了一个全新的搜读写体验&#xff0c;让知识管理变得更加智能和高效。它不仅是一个工具&#xff0c;更是一个智能的伙伴&#xff0c;能够帮助…

NVIDIA Isaac Sim 仿真平台体验测评

目录 一、引言二、GPU加速相关体验2.1 Isaac Sim GPU 加速体验2.2 GPU加速体验分析 三、AI框架集成相关体验四、学术研究价值五、开发生态六、综合分析6.1 主要优势6.1.1 仿真效率6.1.2 开发便利性6.1.3 与 AI 框架的协同性 6.2 潜在应用场景 七、运行体验与建议7.1 GPU加速与P…