当前位置: 首页 > news >正文

159 Linux C++ 通讯架构实战14,epoll 函数代码实战

ngx_epoll_init函数的调用

    //(3.2)ngx_epoll_init函数的调用(要在子进程中执行)
    //四章,四节 project1.cpp:nginx中创建worker子进程;
    //nginx中创建worker子进程
    //官方nginx ,一个master进程,创建了多个worker子进程;
    // master process ./nginx 
    // worker process
    //(i)ngx_master_process_cycle()        //创建子进程等一系列动作
    //(i)    ngx_setproctitle()            //设置进程标题    
    //(i)    ngx_start_worker_processes()  //创建worker子进程   
    //(i)        for (i = 0; i < threadnums; i++)   //master进程在走这个循环,来创建若干个子进程
    //(i)            ngx_spawn_process(i,"worker process");
    //(i)                pid = fork(); //分叉,从原来的一个master进程(一个叉),分成两个叉(原有的master进程,以及一个新fork()出来的worker进程
    //(i)                //只有子进程这个分叉才会执行ngx_worker_process_cycle()
    //(i)                ngx_worker_process_cycle(inum,pprocname);  //子进程分叉
    //(i)                    ngx_worker_process_init();  //ngx_epoll_init函数的调用
    //(i)                        sigemptyset(&set);  
    //(i)                        sigprocmask(SIG_SETMASK, &set, NULL); //允许接收所有信号
    //(i)                        g_socket.ngx_epoll_init();  //初始化epoll相关内容,同时 往监听socket上增加监听事件,从而开始让监听端口履行其职责
    //(i)                            m_epollhandle = epoll_create(m_worker_connections); 
    //(i)                            ngx_epoll_add_event((*pos)->fd....);
    //(i)                                epoll_ctl(m_epollhandle,eventtype,fd,&ev);
    //(i)                    ngx_setproctitle(pprocname);          //重新为子进程设置标题为worker process
    //(i)                    for ( ;; ) {

                                        ngx_process_events_and_timers(); //处理网络事件和定时器事件

                              }. ....                   //子进程开始在这里不断的死循环

    //(i)    sigemptyset(&set); 
    //(i)    for ( ;; ) {}.                //父进程[master进程]会一直在这里循环

ngx_epoll_init 函数内容

该函数从完成的事情有:

1.epoll_create  创建红黑树

2.m_pconnections = new ngx_connection_t[m_connection_n];  创建连接池 并初始化连接池

3.遍历所有监听socket【监听端口】,我们为每个监听socket增加一个 连接池中的连接

for(pos = m_ListenSocketList.begin(); pos != m_ListenSocketList.end(); ++pos){

c = ngx_get_connection((*pos)->fd); //从连接池中获取一个空闲连接对象

c->listening = (*pos);   //连接对象 和监听对象关联,方便通过连接对象找监听对象

(*pos)->connection = c;  //监听对象 和连接对象关联,方便通过监听对象找连接对象

        //对监听端口的读事件设置处理方法,因为监听端口是用来等对方连接的发送三路握手的,所以监听端口关心的就是读事件

        c->rhandler = &CSocekt::ngx_event_accept;

        //往监听socket上增加监听事件,从而开始让监听端口履行其职责【如果不加这行,虽然端口能连上,但不会触发ngx_epoll_process_events()里边的epoll_wait()往下走】if(ngx_epoll_add_event((*pos)->fd,       //socekt句柄1,0,             //读,写【只关心读事件,所以参数2:readevent=1,而参数3:writeevent=0】0,               //其他补充标记EPOLL_CTL_ADD,   //事件类型【增加,还有删除/修改】c                //连接池中的连接 ) == -1) {exit(2); //有问题,直接退出,日志 已经写过了}

}

4.ngx_epoll_add_event 函数 本质上就是epoll_ctl。并设置了回调函数。

#和网络相关
[Net]
#监听的端口数量,一般都是1个,当然如果支持多于一个也是可以的
ListenPortCount = 2
#ListenPort+数字【数字从0开始】,这种ListenPort开头的项有几个,取决于ListenPortCount的数量,
ListenPort0 = 80
ListenPort1 = 443#epoll连接的最大数【是每个worker进程允许连接的客户端数】,实际其中有一些连接要被监听socket使用,实际允许的客户端连接数会比这个数小一些
worker_connections = 1024

//初始化函数【fork()子进程之前干这个事】
//成功返回true,失败返回false
bool CSocekt::Initialize()
{ReadConf();  //读配置项bool reco = ngx_open_listening_sockets();  //打开监听端口return reco;
}//专门用于读各种配置项
void CSocekt::ReadConf()
{CConfig *p_config = CConfig::GetInstance();m_worker_connections = p_config->GetIntDefault("worker_connections",m_worker_connections); //epoll连接的最大项数m_ListenPortCount    = p_config->GetIntDefault("ListenPortCount",m_ListenPortCount);       //取得要监听的端口数量return;
}

    //连接池: 数组,元素数量就是worker_connections【1024】,每个数组元素类型为 ngx_connection_t【结构】; ---结构数组;
     //为什么要引入这个数组:  2个监听套接字, 用户连入进来,每个用户多出来一个套接字;
       //把 套接字数字跟一块内存捆绑,达到的效果就是将来我通过这个套接字,就能够把这块内存拿出来;

初始化 连接池

当执行完成如下的代码后,参考图

    do {i--;   //注意i是数字的末尾,从最后遍历,i递减至数组首个元素//好从屁股往前来---------c[i].data = next;         //设置连接对象的next指针,注意第一次循环时next = NULL;c[i].fd = -1;             //初始化连接,无socket和该连接池中的连接【对象】绑定c[i].instance = 1;        //失效标志位设置为1【失效】,此句抄自官方nginx,这句到底有啥用,后续再研究c[i].iCurrsequence = 0;   //当前序号统一从0开始//----------------------next = &c[i]; //next指针前移} while (i); //循环直至i为0,即数组首地址m_pfree_connections = next;            //设置空闲连接链表头指针,因为现在next指向c[0],注意现在整个链表都是空的m_free_connection_n = m_connection_n;  //空闲连接链表长度,因为现在整个链表都是空的,这两个长度相等;

ngx_get_connection()重要函数:从连接池中找空闲连接;注意在这段代码中 应该只是给 监听fd 一个空闲连接对象。

	for(pos = m_ListenSocketList.begin(); pos != m_ListenSocketList.end(); ++pos){c = ngx_get_connection((*pos)->fd); //从连接池中获取一个空闲连接对象

理论上,后面应该要给每一个connectfd也要给一个 空闲连接对象。

    //c)ngx_epoll_add_event() ************
    //    epoll_ctl();
    //d)ev.data.ptr = (void *)( (uintptr_t)c | c->instance);    把一个指针和一个位 合二为一,塞到一个void *中去,
         //后续能够把这两个值全部取出来,如何取,取出来干嘛,后续再说;

    //ps -eo pid,ppid,sid,tty,pgrp,comm,stat,cmd | grep -E 'bash|PID|nginx'

    //如下命令用root权限执行
    //sudo su       获得root权限
    //lsof -i:80    列出哪些进程在监听80端口
    //netstat -tunlp | grep 80

    //总结:
    //a)epoll_create();epoll_ctl();
    //b)连接池技巧ngx_get_connection(),ngx_free_connection();学习这种编程方法;
    //c)同时传递 一个指针和一个二进制数字技巧;

    //(3.2)ngx_epoll_init函数的调用(要在子进程中执行)
    //四章,四节 project1.cpp:nginx中创建worker子进程;
    //nginx中创建worker子进程
    //官方nginx ,一个master进程,创建了多个worker子进程;
    // master process ./nginx 
    // worker process
    //(i)ngx_master_process_cycle()        //创建子进程等一系列动作
    //(i)    ngx_setproctitle()            //设置进程标题    
    //(i)    ngx_start_worker_processes()  //创建worker子进程   
    //(i)        for (i = 0; i < threadnums; i++)   //master进程在走这个循环,来创建若干个子进程
    //(i)            ngx_spawn_process(i,"worker process");
    //(i)                pid = fork(); //分叉,从原来的一个master进程(一个叉),分成两个叉(原有的master进程,以及一个新fork()出来的worker进程
    //(i)                //只有子进程这个分叉才会执行ngx_worker_process_cycle()
    //(i)                ngx_worker_process_cycle(inum,pprocname);  //子进程分叉
    //(i)                    ngx_worker_process_init();
    //(i)                        sigemptyset(&set);  
    //(i)                        sigprocmask(SIG_SETMASK, &set, NULL); //允许接收所有信号
    //(i)                        g_socket.ngx_epoll_init();  //初始化epoll相关内容,同时 往监听socket上增加监听事件,从而开始让监听端口履行其职责
    //(i)                            m_epollhandle = epoll_create(m_worker_connections); 
    //(i)                            ngx_epoll_add_event((*pos)->fd....);
    //(i)                                epoll_ctl(m_epollhandle,eventtype,fd,&ev);
    //(i)                    ngx_setproctitle(pprocname);          //重新为子进程设置标题为worker process
    //(i)                    for ( ;; ) {}. ....                   //子进程开始在这里不断的死循环

    //(i)    sigemptyset(&set); 
    //(i)    for ( ;; ) {}.                //父进程[master进程]会一直在这里循环

上述,我们已经完成了epoll_init,对于 listenfd的 epoll_ctl

那么epoll_wait是什么时候呢?以及对于connectfd的 epoll_ctl以及对于 connectfd的epoll_wait在哪里呢?

ngx_process_events_and_timers(); 这个函数就是做epoll_wait的 //处理网络事件和定时器事件

    g_socket.ngx_epoll_process_events(-1); //-1表示卡着等待把。 -1表示阻塞等待,为什么要阻塞等待,这是 epoll模型决定的,多路IO复用,listenfd wait的时候是阻塞等待。

    //等待事件,事件会返回到m_events里,最多返回NGX_MAX_EVENTS个事件【因为我只提供了这些内存】;//阻塞timer这么长时间除非:a)阻塞时间到达 b)阻塞期间收到事件会立刻返回c)调用时有事件也会立刻返回d)如果来个信号,比如你用kill -1 pid测试//如果timer为-1则一直阻塞,如果timer为0则立即返回,即便没有任何事件//返回值:有错误发生返回-1,错误在errno中,比如你发个信号过来,就返回-1,错误信息是(4: Interrupted system call)//       如果你等待的是一段时间,并且超时了,则返回0;//       如果返回>0则表示成功捕获到这么多个事件【返回值里】int events = epoll_wait(m_epollhandle,m_events,NGX_MAX_EVENTS,timer);

如果是读事件,注意这里是的读事件,是listenfd的读事件,对应的是 客户端有连接过来了。

对应的回调函数是:   ngx_event_accept; 该设定在前面

        if(revents & EPOLLIN)  //如果是读事件{//一个客户端新连入,这个会成立//c->r_ready = 1;               //标记可以读;【从连接池拿出一个连接时这个连接的所有成员都是0】            (this->* (c->rhandler) )(c);    //注意括号的运用来正确设置优先级,防止编译出错;【如果是个新客户连入//如果新连接进入,这里执行的应该是CSocekt::ngx_event_accept(c)】            //如果是已经连入,发送数据到这里,则这里执行的应该是 CSocekt::ngx_wait_request_handler}

        c->rhandler = &CSocekt::ngx_event_accept;

void CSocekt::ngx_event_accept(lpngx_connection_t oldc) 函数调用:

通过accept4函数得到 connectfd。注意这里都是NON_BLOCK,取到值

        if(use_accept4){s = accept4(oldc->fd, &mysockaddr, &socklen, SOCK_NONBLOCK); //从内核获取一个用户端连接,最后一个参数SOCK_NONBLOCK表示返回一个非阻塞的socket,节省一次ioctl【设置为非阻塞】调用}else{s = accept(oldc->fd, &mysockaddr, &socklen);}

设置copnnectfd的 回调函数为 ngx_wait_request_handler函数

通过ngx_epoll_add_event,将 connectfd 加入红黑树。 这时候如果客户端有数据发送过来,就会调用 ngx_wait_request_handler 函数

        newc->rhandler = &CSocekt::ngx_wait_request_handler;  //设置数据来时的读处理函数,其实官方nginx中是ngx_http_wait_request_handler()//客户端应该主动发送第一次的数据,这里将读事件加入epoll监控if(ngx_epoll_add_event(s,                 //socket句柄1,0,              //读,写EPOLLET,          //其他补充标记【EPOLLET(高速模式,边缘触发ET)】EPOLL_CTL_ADD,    //事件类型【增加,还有删除/修改】                                    newc              //连接池中的连接) == -1)

也就是说,当客户端有数据发送过来的时候,会调用 ngx_wait_Request_handler函数。在这个函数中

1. 收包,这里就要涉及到一个问题,当我们

相关文章:

159 Linux C++ 通讯架构实战14,epoll 函数代码实战

ngx_epoll_init函数的调用 //&#xff08;3.2&#xff09;ngx_epoll_init函数的调用&#xff08;要在子进程中执行&#xff09; //四章&#xff0c;四节 project1.cpp&#xff1a;nginx中创建worker子进程&#xff1b; //nginx中创建worker子进程 //官方nginx ,一个…...

【鹅厂摸鱼日记(一)】(工作篇)认识八大技术架构

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:重生之我在鹅厂摸鱼⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习更多知识   &#x1f51d;&#x1f51d; 认识八大架构 1. 前言2. 架构简介&…...

CA根证书——https安全保障的基石

HTTPS通信中&#xff0c;服务器端使用数字证书来证明自己的身份。客户端需要验证服务器发送的证书的真实性。这就需要一个可信的第三方机构&#xff0c;即CA&#xff0c;来颁发和管理证书。CA根证书是证书颁发机构层次结构的顶级证书&#xff0c;客户端信任的所有证书都可以追溯…...

Spark-Scala语言实战(10)

在之前的文章中&#xff0c;我们学习了如何在spark中使用RDD的filter,distinct,intersection三种方法。想了解的朋友可以查看这篇文章。同时&#xff0c;希望我的文章能帮助到你&#xff0c;如果觉得我的文章写的不错&#xff0c;请留下你宝贵的点赞&#xff0c;谢谢。 Spark-…...

【C++庖丁解牛】高阶数据结构---红黑树详解(万字超详细全面介绍红黑树)

&#x1f341;你好&#xff0c;我是 RO-BERRY &#x1f4d7; 致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f384;感谢你的陪伴与支持 &#xff0c;故事既有了开头&#xff0c;就要画上一个完美的句号&#xff0c;让我们一起加油 目录 前言1.红黑树的概念2.红黑…...

汽车网络安全管理

汽车网络安全管理 我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 屏蔽力是信息过载时代一个人的特殊竞争力&#xff0c;任何消耗你的人和事&#xff0c…...

文本自动粘贴编辑器:支持自动粘贴并筛选手机号码,让信息处理更轻松

在信息时代的浪潮中&#xff0c;文本处理已成为我们日常工作与生活的重要组成部分。无论是商务沟通、社交互动还是个人事务处理&#xff0c;手机号码的筛选与粘贴都显得尤为关键。然而&#xff0c;传统的文本处理方式效率低下、易出错&#xff0c;已无法满足现代人的高效需求。…...

Linux云计算之网络基础9——园区网络架构项目

要求构建大型园区网络架构&#xff0c;方案如下&#xff1a; 园区A 园区c 公司B 要求&#xff1a; 1、A公司园区网络 一台汇聚层三层交换机&#xff0c;两台接入层二层交换机。 出口有一台路由器。 2、A园区有五台服务器。 分别为两台 WEB 服务器&#xff0c;…...

Java 中的 List 集合

文章目录 添加元素获取元素检查元素删除元素修改元素获取列表大小检查列表是否为空清空列表查找元素索引获取列表的子列表 List 是 Java 集合框架中的一个接口&#xff0c;它表示一个有序的集合&#xff08;序列&#xff09;&#xff0c;允许存储重复的元素。List 接口提供了许…...

数据库之DDL操作(数据库,表,字段)

Data Definition Language&#xff0c;数据库定义语言&#xff0c;用来定义数据库对象&#xff08;数据库&#xff0c;表&#xff0c;字段&#xff09; 1.数据库操作 1.1查询所有数据库 show databases; 1.2查询当前数据库 show databases(); 1.3创建数据库 create da…...

5.3.1 配置交换机 SSH 管理和端口安全

5.3.1 实验1:配置交换机基本安全和 SSH管理 1、实验目的 通过本实验可以掌握&#xff1a; 交换机基本安全配置。SSH 的工作原理和 SSH服务端和客户端的配置。 2、实验拓扑 交换机基本安全和 SSH管理实验拓扑如图所示。 交换机基本安全和 SSH管理实验拓扑 3、实验步骤 &a…...

Django--数据库连接

数据库配置 打开mysite/settings.py配置文件&#xff0c;这是整个Django项目的设置中心。Django默认使用SQLite3数据库&#xff0c;因为Python原生支持SQLite3数据库&#xff0c;所以你无须安装任何程序&#xff0c;就可以直接使用它。 下面是默认的数据库配置&#xff1a; …...

CKA 基础操作教程(二)

Kubernetes Deployment 理论学习 Kubernetes Deployment &#xff08;部署&#xff09;是一种 Kubernetes 资源对象&#xff0c;用于定义和管理容器化应用程序的部署和更新。Deployment 提供了一种声明性的方式来定义应用程序的期望状态&#xff0c;并负责确保所需数量的 Pod…...

【SQLServer】快速查看SQL Server中所有数据库中所有表的行数

1.查看某个数据库中每个表的行数 SELECT @@servername as servername, db_name() as databasename, s.name AS schemaname, t.name AS tablename,p.rows AS rowcounts,SUM(a...

Node.js------Express

◆ 能够使用 express.static( ) 快速托管静态资源◆ 能够使用 express 路由精简项目结构◆ 能够使用常见的 express 中间件◆ 能够使用 express 创建API接口◆ 能够在 express 中启用cors跨域资源共享 一.初识Express 1.Express 简介 官方给出的概念&#xff1a;Express 是基…...

CSS - 你实现过0.5px的线吗

难度级别:中级及以上 提问概率:75% 我们知道在网页显示或是网页打印中,像素已经是最小单位了,但在很多时候,即便是最小的1像素,精度却不足以呈现所需的线条精度和细节。因此,为了在网页显示和网页打印中呈现更加细致的线条,为了在视觉…...

hbuilderX创建的uniapp项目转移到vscode

场景&#xff1a;一直使用hbuilderX开发的朋友想转移到vscode获取更好的TypeScript支持&#xff0c;所以想把整个项目目录拖到vscode进行开发&#xff0c;但发现运行不了&#xff0c;提示没有package.json等&#xff0c;并且不能执行pnpm命令 首先&#xff0c;我们先来看一下h…...

JavaScript 事件流

JavaScript与HTML之间的交互是通过事件实现的&#xff0c;而用户与浏览器页面的互动也是通过事件来实现的事件就是文档或浏览器窗口中发生的一些特定的交互瞬间&#xff0c;所以分为两种事件&#xff0c;一是发生在 浏览器对象&#xff08;BOM&#xff09;上的事件&#xff0c;…...

HTML——5.表单、框架、颜色

一、表单 HTML 表单用于在网页中收集用户输入的数据&#xff0c;例如登录信息、搜索查询等。HTML 提供了一系列的表单元素&#xff0c;允许用户输入文本、选择选项、提交数据等。 <!DOCTYPE html> <html lang"en"> <head> <meta charset&q…...

Docker、Kubernetes之间的区别

比较容器化工具&#xff1a;了解 Docker、Kubernetes 在应用程序部署和管理方面的差异。 基本概述 Docker 是一个流行的容器化平台&#xff0c;允许开发人员在容器中创建、部署和运行应用程序。 Docker 提供了一组工具和 API&#xff0c;使开发人员能够构建和管理容器化应用程…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻

在如今就业市场竞争日益激烈的背景下&#xff0c;越来越多的求职者将目光投向了日本及中日双语岗位。但是&#xff0c;一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧&#xff1f;面对生疏的日语交流环境&#xff0c;即便提前恶补了…...

Ubuntu系统下交叉编译openssl

一、参考资料 OpenSSL&&libcurl库的交叉编译 - hesetone - 博客园 二、准备工作 1. 编译环境 宿主机&#xff1a;Ubuntu 20.04.6 LTSHost&#xff1a;ARM32位交叉编译器&#xff1a;arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编译工具链 在交叉编译之前&#x…...

Linux 文件类型,目录与路径,文件与目录管理

文件类型 后面的字符表示文件类型标志 普通文件&#xff1a;-&#xff08;纯文本文件&#xff0c;二进制文件&#xff0c;数据格式文件&#xff09; 如文本文件、图片、程序文件等。 目录文件&#xff1a;d&#xff08;directory&#xff09; 用来存放其他文件或子目录。 设备…...

TDengine 快速体验(Docker 镜像方式)

简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能&#xff0c;本节首先介绍如何通过 Docker 快速体验 TDengine&#xff0c;然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker&#xff0c;请使用 安装包的方式快…...

云计算——弹性云计算器(ECS)

弹性云服务器&#xff1a;ECS 概述 云计算重构了ICT系统&#xff0c;云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台&#xff0c;包含如下主要概念。 ECS&#xff08;Elastic Cloud Server&#xff09;&#xff1a;即弹性云服务器&#xff0c;是云计算…...

在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:

在 HarmonyOS 应用开发中&#xff0c;手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力&#xff0c;既支持点击、长按、拖拽等基础单一手势的精细控制&#xff0c;也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档&#xff0c…...

(二)TensorRT-LLM | 模型导出(v0.20.0rc3)

0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述&#xff0c;后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作&#xff0c;其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...

2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面

代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口&#xff08;适配服务端返回 Token&#xff09; export const login async (code, avatar) > {const res await http…...

什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南

文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/55aefaea8a9f477e86d065227851fe3d.pn…...

力扣-35.搜索插入位置

题目描述 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...