基于多反应堆的高并发服务器【C/C++/Reactor】(中)创建一个TcpConnection实例

news2024/11/17 6:44:00

#CSDN 年度征文|回顾 2023,赢专属铭牌等定制奖品#

一、主线程反应堆模型的事件添加和处理详解 

>>服务器和客户端建立连接和通信流程:

基于多反应堆模型的服务器结构图,这主要是一个TcpServer,关于HttpServer,主要是用了Http协议,核心模块是TcpServer。这里边有两种线程:主线程和子线程。子线程是在线程池里边,线程池的每个子线程都有一个反应堆模型,每个反应堆模型都需要有一个TcpConnection

如果这个反应堆实例所属的线程是主线程,主线程是如何在这个反应堆模型里边工作的呢?在服务器端有一个用于监听的文件描述符ListenFd(简写为lfd),基于lfd就可以和客户端建立连接,如果想要让lfd去工作,就得把它放到反应堆模型里边,首先要对lfd封装成Channel类型,之后添加到TaskQueue这个任务队列里边,接着MainEventLoop就会遍历TaskQueue,取出对应的任务节点(ChannelElement),基于任务节点里边的type对这个节点进行添加/删除/修改操作。

补充说明:取出这个节点之后,判断这个节点的类型type,如果type==ADD,把channel里边的文件描述符fd添加到Dispatcher的检测集合中;如果type==DELETE,channel里边的文件描述符fdDispatcher的检测集合中删除;如果type==MODIFY,把channel里边的文件描述符fdDispatcher的检测集合中的事件进行修改。主线程往属于自己的反应堆模型里边放的文件描述符是用于监听的,那么这个lfd肯定是要添加到Dispatcher的检测集合里边,所以操作肯定是添加操作(ADD)。

很显然,这个lfd需要添加到反应堆模型的Dispatcher里边,Dispatcher主要封装了poll/epoll/select模型,不管使用了这三个里边的哪一个,其实都需要对用于监听的文件描述符的读事件进行检测。在检测的时候,如果是epoll模型,它会调用epoll_wait函数; 如果是poll模型,它会调用poll函数;如果是select模型,它会调用select函数;通过这三个函数,传出的数据,我们就能够知道用于监听的文件描述符lfd它对应的读事件触发了。对应的读事件触发了,就可以基于得到的文件描述符(此处为lfd)。通过ChannelMap里边的fdfd其实就是数组的下标)可以找到对应的channel地址,那么基于lfd就可以找到对应的channel地址,就能知道lfd所对应的读事件要干什么。也就是和客户端建立连接,也就可以得到一个通信的文件描述符(cfd)。

首先把用于通信的文件描述符封装成一个Channel类型,接着把channel封装到TcpConnection模块里边。另外,这个TcpConnection模块需要在子线程里边运行的,故需要通过子线程去访问线程池,从线程池找出一个子线程,每个子线程都有一个EventLoop,再把子线程的EventLoop也放到我们封装的TcpConnection模块里边。也就是把子线程的反应堆实例传给TcpConnection模块。

一定要注意:TcpConnection模块里边的EventLoop是属于子线程的,是从子线程传过来的一个反应堆模型的地址。然后就可以在TcpConnection模块里边通过Channel里边封装的通信的文件描述符(cfd)和客户端进行通信,就是接收数据和发送数据。关于通信的文件描述符的事件检测,读事件或者是写事件检测都是通过EventLoop来实现的。

二、创建一个TcpConnection实例

每个通信的文件描述符都对应一个TcpConnection,并且每个TcpConnection都对应一个子线程。假设说我现在有10TcpConnection,4个线程,那么每个通信的文件描述符所对应的TcpConnectionName是不一样的。但是,有可能有若干个TcpConnection是在同一个子线程里边执行的。在处理任务时,进行套接字通信的进程个数是有限的。

关于任务的分配:加入有个任务,但是只有4个线程,那么把第一个任务给第一个子线程,再把第二个任务给第二个子线程,再把第三个任务给第三个子线程,再把第四个任务给第四个子线程。而把第五个任务就给到第一个子线程,把第六个任务给到第二个子线程,以此类推。

所以不同的TcpConnection有可能是在同一个线程里边被处理的,但是每个TcpConnection里边都有一个用于通信的文件描述符,这个文件描述符对应的连接的名字(Name)是唯一的。如果你发现出现相同的名字的,除非是这个文件描述符通信完了之后被释放了,而我们又建立了新的连接。被释放的这个文件描述符被复用了,所以我们就会发现当前的这个文件描述符对应的连接的名字和之前的某个文件描述符对应的连接的名字是相同的。

Name:用于标识每个连接的名称。当文件描述符被释放时,可以被重用,因此可能存在名称相同的连接。

struct TcpConnection {
    struct EventLoop* evLoop;
    struct Channel* channel;
    struct Buffer* readBuf;
    struct Buffer* writeBuf;
    char name[32];
};

(1)创建一个TcpConnection实例

// 初始化
struct TcpConnection* tcpConnectionInit(int fd,struct EventLoop* evLoop);
// 初始化
struct TcpConnection* tcpConnectionInit(int fd,struct EventLoop* evLoop) {
    struct TcpConnection* conn = (struct TcpConnection*)malloc(sizeof(struct TcpConnection));
    conn->evLoop = evLoop;
    struct Channel* channel = channelInit(fd,ReadEvent,processRead,NULL,conn);
    conn->channel = channel;
    conn->readBuf = bufferInit(10240); // 10k
    conn->writeBuf = bufferInit(10240); // 10k
    sprintf(conn->name,"TcpConnection-%d",fd);
    // 把channel添加到事件循环对应的任务队列里边
    eventLoopAddTask(evLoop,conn->channel,ADD);
    return conn;
}

第一步:channel初始化 

  • 其中,会把用于通信的文件描述符cfd作为参数传入tcpConnectionInit里去,也就是fd为用于通信的文件描述符。将fd封装成channel。需要检测文件描述符什么事件呢?在服务器端通过文件描述符fd和客户端通信,如果客户端不给服务器发数据,服务器就不会给客户端回数据。因此在服务器端迫切想知道的有没有数据到达:就是有没有发过来请求数据。关于这个读事件我们需要指定一个processRead回调函数。
struct Channel* channel = channelInit(fd,ReadEvent,processRead,NULL,conn);

 第二步:把channel添加到事件循环对应的任务队列里边去

eventLoopAddTask(evLoop,conn->channel,ADD);

(2)接收客户端数据 => processRead回调函数

  • 回顾Buffer模块的接收套接字数据 bufferSocketRead函数
// 写内存 2.接收套接字数据
int bufferSocketRead(struct Buffer* buf,int fd);
  1. bufferSocketRead函数实现功能:当调用这个bufferSocketRead函数之后,一共接收到了多少个字节
  2. bufferSocketRead函数具体细节:在这个函数里边,通过malloc申请了一块临时的堆内存(tmpbuf),这个堆内存是用来接收套接字数据的。当buf里边的数组容量不够了,那么就使用这块临时内存来存储数据,还需要把tmpbuf这块堆内存里边的数据再次写入到buf中。当用完了之后,需要释放内存。
  • processRead回调函数 
// 接收客户端数据
int processRead(void* arg) {
    struct TcpConnection* conn = (struct TcpConnection*)arg;
    // 接收数据
    int count = bufferSocketRead(conn->readBuf,conn->channel->fd);
    if(count > 0) {
        // 接收到了Http请求,解析Http请求
        ...(待续写)
    }else {
        // 断开连接
        ...(待续写)
    }
}

未完待续~

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

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

相关文章

torch.meshgrid和np.meshgrid的区别

numpy中meshgrid: 把数组a当作一行,再根据数组b的长度扩充行。 把数组b当作一列,再根据数组a的长度扩充列。 torch中meshgrid: 把数组a当作一列,再根据数组b的长度扩充列。 把数组b当作一行,再根据数组a的…

外汇天眼:交易如何突破“知行合一”这关?

接触交易之后有无数次想要放弃交易,在交易中的失败实在是太痛苦了,有时候这种失败是打击的作为一个人的最根本的自信,这种失败让我质疑我自己“本就是个普通人,不要想太美的事情”“为什么学习这么多还是不能盈利,我真…

LeetCode-无重复字符的最长子串(3)

题目描述&#xff1a; 给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长子串 的长度。 代码&#xff1a; class Solution {public int lengthOfLongestSubstring(String s) {Set<Character> occnew HashSet<Character>();int lens.length();int…

Java集合框架深度解析-ArrayList

Java的集合框架提供了一组实现常用数据结构的类和接口。理解集合框架对于Java程序员来说至关重要&#xff0c;因为它们在日常编程中广泛应用。 为什么需要集合框架&#xff1f; 在编程中&#xff0c;我们经常需要存储和操作一组对象。集合框架提供了用于表示和操作对象组的通…

如何使用 Python 解决网络抓取中的 reCAPTCHA 问题

在网络抓取的领域&#xff0c;开发人员经常面临 reCAPTCHA 的障碍。为了区分人类和自动化机器人&#xff0c;reCAPTCHA 可能会成为那些试图从网站提取数据的人的沉痛阻碍。然而&#xff0c;借助 Python 和像 Capsolver 这样的工具&#xff0c;可以绕过 reCAPTCHA 并继续抓取有价…

【计算机病毒传播模型】报告:区块链在车联网中的应用

区块链在车联网中的应用 写在最前面题目 - 26 车联网安全汇报演讲稿-删减2后&#xff0c;最终版&#xff08;1469字版本&#xff09;汇报演讲稿-删减1后&#xff08;2555字版本&#xff09;汇报演讲稿-删减前&#xff08;3677字版本&#xff09;1 概述1.1 车联网1.2 区块链1.3 …

【C#】网址不进行UrlEncode编码会存在一些问题

欢迎来到《小5讲堂》&#xff0c;大家好&#xff0c;我是全栈小5。 这是2024年第3篇文章&#xff0c;此篇文章是C#知识点实践序列文章&#xff0c;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不对之处望指正&#xff01; 目录 前言数据丢失效果请求端代码接口端代码…

【深度学习:Self-supervised learning (SSL) 】自我监督学习解释

【深度学习&#xff1a;SSL Self-supervised learning 】自我监督学习解释 什么是自我监督学习&#xff1f;比较自我监督学习与监督学习和无监督学习 为什么计算机视觉模型需要自监督学习&#xff1f;自我监督学习的好处自监督学习的局限性 自我监督学习如何运作&#xff1f;对…

burpsuite 爆破

靶场搭建:phpstudy的安装与靶场搭建 - junlin623 - 博客园 (cnblogs.com) 账号字典:XXTK: 一些弱口令、fuzz字典 (gitee.com) 网盘链接:https://pan.baidu.com/s/1v5pAwaTwoeCnJgkUXf3iLQ?pwd=mllm 提取码:mllm --来自百度网盘超级会员V2的分享 一、暴力破解 - 基于…

Linux-端口、nmap命令、netstat命令

端口是设备与外界通讯交流的出入口&#xff0c;可分为物理端口和虚拟端口 物理端口实际存在可以看见&#xff0c;而虚拟端口是指计算机内部的端口&#xff0c;是不可见的&#xff0c;用来操作系统和外部交互使用。 IP地址不能锁定程序&#xff0c;所以可以通过端口&#xff0…

全网最全fiddler使用教程和fiddler如何抓包(fiddler手机抓包)-笔者亲测

一、前言 抓包工具有很多&#xff0c;比如常用的抓包工具Httpwatch&#xff0c;通用的强大的抓包工具Wireshark.为什么使用fiddler?原因如下&#xff1a; 1.Wireshark是通用的抓包工具&#xff0c;但是比较庞大&#xff0c;对于只需要抓取http请求的应用来说&#xff0c;似乎…

线性代数——(期末突击)行列式(上)-行列式计算、行列式的性质

目录 行列式 行列式计算 逆序数 行列式的性质 转置 两行&#xff08;列&#xff09;互换 两行&#xff08;列&#xff09;对应相等 提公因子 两行&#xff08;列&#xff09;对应成比例 某行&#xff08;列&#xff09;为零 行列式分裂 行列式变换及三角行列式 行…

Selenium库和ChromeDriver谷歌驱动最新版安装

1.安装selenium库 使用pip安装第三方库selenium&#xff0c;速度较慢。 pip install selenium 使用国内清华源安装第三方库selenium&#xff0c;速度较快。 pip install selenium -i https://pypi.tuna.tsinghua.edu.cn/simple 2.安装谷歌浏览器驱动 驱动下载链接&#x…

低代码UI构件程序设计基础实训一

步骤一 低代码在线调试工具 网址 低代码在线调试工具 步骤二 D盘建一个文件夹&#xff0c;文件夹下建两个文件夹ico和js 网上找一些图片&#xff0c;后缀.png&#xff0c;编号从0开始&#xff0c;图片另存到ico文件夹下 下载&#xff08;右键另存为&#xff09;以下四个页面…

物流实时数仓:数仓搭建(DWS)二

系列文章目录 物流实时数仓&#xff1a;采集通道搭建 物流实时数仓&#xff1a;数仓搭建 物流实时数仓&#xff1a;数仓搭建&#xff08;DIM&#xff09; 物流实时数仓&#xff1a;数仓搭建&#xff08;DWD&#xff09;一 物流实时数仓&#xff1a;数仓搭建&#xff08;DWD&am…

7nm项目之顶层规划——04 power routing and pushdown

1.设计数据导入&#xff08;见01&#xff09; 2.初始化 top floorplan with def 3.创建 block partition 4.调整 block floorplan (size/location/area/connection, manul work) 5.format floorplan size and location 6.create tracks 7.pin assignment 8.power routi…

<sa8650>sa8650 qcxser-之-QCX错误报告接口

<sa8650>sa8650 qcxser-之-QCX错误报告接口 1 前言2 错误报告设计3 报告错误的QCarCam APIs3.1 错误ID3.2 错误code3.3 错误源4 错误报告流1 前言 本章主要讲解QCX服务的错误报告接口,如何将qcxserver的错误诊断信息报告给Safety Monitor。 2 错误报告设计 图2-1显示了通…

Java实现阿里云OSS文件上传

1、OSS介绍 阿里云对象存储OSS&#xff08;Object Storage Service&#xff09;是一款云存储服务&#xff0c;通常用于图片、音视频、日志等海量文件的存储&#xff0c;并且数据以对象&#xff08;Object&#xff09;的形式存储在OSS的存储空间&#xff08;Bucket &#xff09…

Centos7.9或Deebian12安装K3s和k9s详细流程

1、在线安装k3s 安装的版本为&#xff1a;v1.23.15k3s1 curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | INSTALL_K3S_MIRRORcn INSTALL_K3S_VERSION"v1.23.15k3s1" sh - 2、安装完成&#xff0c;测试 kubectl get nodes正常输出即没…

使用tensorboard查看loss曲线

1.安装tensorboard pip install tensorboard 如果报错“no module named past”,执行下面的命令安装future pip install future2.在main.py中使用tensorboard绘制loss函数图像 # 导入 from torch.utils.tensorboard import SummaryWriterdef fit(self):for epoch in range(s…