网络编程详解(select poll epoll reactor)

1. 客户端服务器建立连接过程

1.1 编写一个server的步骤是怎么样的?

int main(){int listenfd, connfd;pid_t childpid;socklen_t clilen;struct sockaddr_in cliaddr, servaddr;listenfd = socket(AF_INET, SOCK_STREAM, 0);bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(SERV_PORT);bind(listenfd, (SA *)&servaddr, sizeof(servaddr));listen(listenfd, LISTENQ);for( ; ; ){clilen = sizeof(cliaddr);connfd = accept(listenfd, (SA *)&cliaddr, &clilen);if((childpid = fork()) == 0){close(listenfd);str_echo(connfd);exit(0);}close(connfd);}}
  • serverfd = socket( opt ):调用socket( )方法创建一个对应的serverfd
  • bind( serverfd, address ):调用bind( )方法将fd和指定的地址( ip + port )进行绑定
  • listen( serverfd ):调用listen( )方法监听前面绑定时指定的地址
  • clientfd = accept( serverfd ):进入无限循环等待接受客户端连接请求

1.2 server是怎么处理建立连接后的client请求的?

void str_echo(int sockfd){ssize_t n;char buf[MAXLINE];
again:while((n = read(sockfd, buf, MAXLINE)) > 0)  // 从client读数据writen(sockfd, buf, n);  // 给client写数据if(n < 0 && errno == EINTR)goto again;else if(n < 0)err_sys("str_echo: read error");
}
  • n = read( clientfd, buf, size ):从客户端clientfd里读取传输进来的数据,并将数据存放到buf中
  • writen( clientfd, buf, n ):往客户端clientfd写出数据n个字节的数据,写出的数据存放在buf中

1.3 server和client完整交互过程

![[1712023405114.png]]

2.网络演变过程

2.1 演变的本质

在这里插入图片描述

2.2 阻塞IO:Blocking IO

在这里插入图片描述

  • 阻塞io:在内核中发生两次阻塞,一个是没有数据就绪的时候会发生阻塞,另一个是数据准备就绪的时候将数据从内核态copy到用户态的时候会阻塞
  • 优点:
    • 可以实现client和server端通信
    • 实现简单,通常一个client连接分配一个线程进行处理
  • 缺点:
    • 能支持的并发client连接数较少,因为一台server能分配的线程是有限的,8个核最多能开8个线程;并且大量线程会造成上下文切换过多而影响性能

2.3 非阻塞IO:Nonblocking IO

  • 核心矛盾:之所以一个client连接分配一个线程是因为处理客户端的读写时阻塞式的,为避免该阻塞影响后续接收新的client连接,所以将阻塞逻辑交由单独线程处理
    在这里插入图片描述

  • 非阻塞io:上层应用每过一段时间就向内核询问是否有数据就绪,如果没有数据就返回,如果有数据了就会从内核态cpoy数据到用户态

  • 阻塞和非阻塞IO的区别:在于内核中数据尚未就绪时如何处理

    • 对于非阻塞IO,则直接返回给用户态RWOULDBLOCK状态码错误
    • 对于阻塞IO则一直处于阻塞状态,直到数据就绪并从内核态拷贝到用户态后才返回
  • 如何设置非阻塞

    • 方法1:
      • 通过socket( )方法中的type参数来指定为SOCK_NONBLOCK即可设置该socket为非阻塞方式
      • int socket( int domain, int type, int protocol );
    • 方法2:
      • 通过fcntl( )方法中args参数设置为O_NONBLOCK即可设置该socket为非阻塞方式
      • int fcntl( int fd, int cmd, … /*arg*/ );
      • fcntl( socket_fd, F_SETFL, flags | O_NONBLOCK )
  • 非阻塞的优缺点:

    • 优点:将socket设置成非阻塞后,在读取时如果数据未就绪就直接返回,得益于非阻塞的特性可以通过一个线程管理多个client连接
    • 缺点:需要不断轮询询问内核数据是否已经就绪,涉及很多无效的频繁的系统调用

2.4 IO多路复用第一版:select poll

  • 核心矛盾:涉及很多次无用的平凡的系统调用,非阻塞socket在read时并不知道什么时候数据会准备好,所以需要不断的主动询问
    在这里插入图片描述

  • 所谓io多路复用:

    • 网上大多数的观点是可以使用单个线程管理多个客户端的连接
    • 另一个个人观点说io多路复用的是系统调用,原先是一个客户端通过一个系统调用去处理,现在转变成通过一次系统调用select/poll由内核主动通知用户哪些client数据已就绪,大大减少了无效的系统调用次数

select

#include <sys/select.h>
#include <sys/time.h>int select(int maxfd, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timeval *timeout);
  • maxfd:表示被select管理的描述符个数,值为最大描述符+1
  • fd_set:表示一组描述符集合,select中是用一个位数组来实现的,要给描述符占一位
  • readset、writeset、exceptset:可读事件集合、可写事件集合、异常事件集合
  • timeout:等于0立即返回,大于0设置一个超时时间,小于0永远等待

poll

struct pollfd{int fd;short events;   // 关心的事件short revents;  // 发生的事件
};#include <poll.h>int poll(struct pollfd *fdarray, unsigned long nfds, int timeout);
  • poll参数解释:
    • fdarray:为传入的pollfd数组的首地址,该数组中的每一个元素为一个poll结构体镜像,关联一个管理的描述符fd
    • nfds:传入的值为fdarray数组的长度,表示管理的描述符个数,主要原因在于前面的fdarray是一个可变长度的数组,因此需要指定数组长度
    • timeout:无限等待(INFTIM,一个负值)、立即返回不阻塞(0)、等待指定的超时时间(timeout)
  • poll事件定义:四类处理输入事件、三类处理输出事件、三类处理错误事件
  • poll识别三类事件:普通(normal)、优先级带(priority band)、高优先级(priority)
    在这里插入图片描述

select 和 poll 的区别

  • 在实现上
    • select底层实现是采用位数组来实现的,一个描述符对应一位
    • poll底层是通过pollfd结构体来实现的,管理的描述符通过pollfd数组来组织,一个描述符对应一个pollfd对象
  • 在用法上
    • select默认大小是FD_SETSIZE(1024),修改的话需要修改配置参数同时重新编译内核来实现
    • poll是采用变长数组管理的,理论上可以支持海量连接
  • 相同点
    • 二者在调用时,都需要从用户态拷贝管理的全量描述符到内核态,返回时也需要拷贝全量描述符从内核态到用户态,再有用户态遍历全量描述符判断哪些描述符有就绪事件

优缺点

  • 优点:
    • 充分利用了一次系统调用select/poll就可以实现管理多个client事件,大大降低了非阻塞IO频繁无效的系统调用
    • 核心是将主动询问内核转变为等待内核通知,提升性能
  • 缺点:
    • 每次都需要将管理的多个client从用户态拷贝到内核态,在管理百万连接时,由拷贝带来的资源开销较大,影响性能

2.5 IO多路复用第二版:epoll

  • 核心矛盾:select/poll每次都需要将管理的多个client从用户态拷贝到内核态,影响性能
    在这里插入图片描述

epoll三大核心接口

1. epoll_create( )
#include<sys/epoll.h>
int epoll_create(int size);
  • 从linux2.6.8以后,size参数已经被忽略,大于0即可
  • epoll_create( )创建返回的epollfd指向内核中的一个epoll实例,同时该epollfd用来调用所有和epoll相关的接口(epoll_ctl和epoll_wait)
  • 当epollfd不再使用时,需要调用close关闭。当所有指向epoll的文件描述符关闭后,内核会摧毁该epoll实例并释放和其关联的资源
  • 成功会返回大于0的epollfd,失败返回-1
2. epoll_ctl( )
  • 核心思想:将哪个客户端(fd)的哪些事件(event)交给哪个epoll(epfd)来管理(op)
#include<sys/epoll.h>
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
  • epfd:通过epoll_create( )创建的epollfd
  • op:EPOLL_CTL_ADD、EPOLL_CTL_MOD、EPOLL_CTL_DEL
  • fd:待监听的描述符fd
  • event:要监听的fd的时间(读、写、接收连接等),具体如下:
    在这里插入图片描述
3. epoll_wait( )
#include<sys/epoll.h>
int epoll_wait(int epfd, struct epoll_event *event, int maxevents, int timeout);
  • epfd:通过epoll_create( )创建的epollfd
  • events:返回就绪的事件列表,就绪的事件列表个数通过epoll_wait( )的返回值来传递
  • maxevents:最多返回的events个数,该值用来告诉内核创建的events有多大
  • timeout:超时时间
  • 返回值cnt:
    • 0表示超时时间范围内无就绪队列
    • 大于0表示返回就绪列表的个数(后续通过循环遍历events[0]~events[cnt-1])
    • -1表示错误
  • event检测:
    if(event & EPOLLHUP){ ... }
    if(event & (EPOLLPRI | EPOLLERR | EPOLLHUP)){ ... }
    

epoll的ET模式和LT模式区别

在这里插入图片描述

epoll内核实现

在这里插入图片描述
在这里插入图片描述

2.5 异步IO

  • 异步io,两个阶段都不会被阻塞
    在这里插入图片描述

同步IO和异步IO的区别

  • 第二阶段copy阶段,如果是用户线程来完成的就是同步io,如果是内核线程来完成的就是异步io

在这里插入图片描述

3. 主流网络模型

3.1 thread-based架构模型

  • 适用场景:并发量不大的场景

  • 原因:

    • 线程的创建、销毁开销较大
    • 创建的线程需要占用一定的资源
    • 线程切换需要一定的资源开销
    • 一个进程能开辟的线程数据有限
      在这里插入图片描述
  • 对应的是阻塞IO

3.2 single-reactor单线程网络模型

  • 核心:IO中的accept、read、write都是在一个线程完成的
  • 存在问题:目前该模型中,除了IO操作在reactor线程外,业务逻辑处理操作也在reactor线程上,当业务逻辑处理比较耗时时,会大大降低了IO请求的处理效率
  • 典型实现:redis(4.0之前)
    在这里插入图片描述

3.3 single-reactor线程池模型

  • 如何改进:引入了线程池,用来专门处理业务逻辑操作,提升IO响应速度
  • 缺陷:虽然在引入线程池后IO响应速度提升了,但在管理百万级连接、高并发大数据量时,单个reactor线程仍然会效率比较低下
    在这里插入图片描述

3.4 multi-reactor多线程模型

  • 如何改进:保留原先single-reactor引入的线程池外,新扩展了reactor线程。引入了多个reactor线程,也称为主从结构
  • 扩展方法:
    • 单进程(多线程)模式
    • 多进程模式
  • 典型实现:
    • netty
    • memcached

在这里插入图片描述

3.5 multi-reactor多进程模型

  • mainreactor进程主要负责接收客户端连接,并将建立的客户端连接进行分发给subreactor进程中
  • subreactor进程主要负责处理客户端的数据读写和业务逻辑的处理
  • 经典实现:nginx
    在这里插入图片描述

两种multi-reactor模型对比

在这里插入图片描述

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

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

相关文章

Java基础核心Map

在Java中&#xff0c;Map是一种用于存储键值对&#xff08;key-value pairs&#xff09;的集合类型。它提供了一种将键映射到值的方式&#xff0c;其中每个键在Map中都是唯一的。Map接口是java.util包中的一部分。 常用实现类&#xff1a; HashMap: 基于哈希表实现的Map&#…

JavaScript发票查验接口如何集成?返回错误的信息原因是什么呢?

发票查验的过程中&#xff0c;很可能出现各种各样的问题&#xff0c;那么返回错误信息的原因一般可能是因为以下几种原因&#xff1a;第一种是接口没有调通&#xff0c;第二种是本身发票就是一张错票、假票&#xff0c;第三种可能是税局系统或网络问题等等&#xff0c;如果在使…

6、Cocos Creator 2D 渲染组件:​Sprite 组件​

Sprite 组件 Sprite&#xff08;精灵&#xff09;是 2D/3D 游戏最常见的显示图像的方式&#xff0c;在节点上添加 Sprite 组件&#xff0c;就可以在场景中显示项目资源中的图片。 属性功能说明Type渲染模式&#xff0c;包括普通&#xff08;Simple&#xff09;、九宫格&#x…

网易云首页单页面html+css

网页设计与网站建设作业htmlcss 预览 源码查看https://hpc.baicaitang.cn/2083.html

Java | Leetcode Java题解之第4题寻找两个正序数组的中位数

题目&#xff1a; 题解&#xff1a; class Solution {public double findMedianSortedArrays(int[] A, int[] B) {int m A.length;int n B.length;if (m > n) { return findMedianSortedArrays(B,A); // 保证 m < n}int iMin 0, iMax m;while (iMin < iMax) {int…

linux shell命令(进程管理、用户管理)

一、进程的概念 主要有两点&#xff1a; 1.进程是一个实体。每一个进程都有它自己的地址空间&#xff0c;一般情况下&#xff0c;包括文本区域&#xff08;text region&#xff09;、数据区域&#xff08;data region&#xff09;和堆栈&#xff08;stack region&#xff09;…

Visual Studio 2022报错c1083,win11解决办法

如果头文件报错&#xff0c;并且编译器报错是c1083&#xff0c;无法处理的时候&#xff0c;包括卸载重装也是无济于事的时候 此时可以采取一下办法进行修改 出现这个的主要原因是安装 Windows SDK 时版本出错&#xff0c;需要根据自己的 windows 版本选择安装对应版本的 Wind…

SpringBoot+ECharts+Html 地图案例详解

1. 技术点 SpringBoot、MyBatis、thymeleaf、MySQL、ECharts 等 此案例使用的地图是在ECharts社区中查找的&#xff1a;makeapie echarts社区图表可视化案例 2. 准备条件 在mysql中创建数据库echartsdb&#xff0c;数据库中创建表t_location_count表&#xff0c;表中设置两个…

Redis底层数据库之SDS

高速的存储介质&#xff1a;内存优秀的底层数据结构高效的IO模型高效的线程模型 1. 动态字符串SDS Redis中保存的Key是字符串&#xff0c;value往往是字符串或者字符串的集合。可见字符串是redis中最常用的一种数据结构。 C语言种字符串存在的一些问题&#xff1a; 获取字符…

【嵌入式智能产品开发实战】(十四)—— 政安晨:通过ARM-Linux掌握基本技能【链接静态库与动态库】

目录 链接静态库 动态链接 与地址无关的代码 全局偏移表 延迟绑定 共享库 政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 收录专栏: 嵌入式智能产品开发实战 希望政安晨的博客能够对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论…

初阶数据结构—算法的时间复杂度和空间复杂度

第一章&#xff1a;数据结构前言&#xff08;Lesson 1&#xff09; 1. 什么是数据结构&#xff1f; 数据结构 (Data Structure) 是计算机存储、组织数据的方式&#xff0c;指相互之间存在一种或多种特定关系的 数据元素的集合。 2. 什么是算法&#xff1f; 算法(Algorithm)…

【数据处理包Pandas】多级索引的创建及使用

目录 一、元组作为一级索引&#xff08;一&#xff09;示例1&#xff08;二&#xff09;示例2 二、引入多级索引&#xff08;一&#xff09;多级索引的创建&#xff08;二&#xff09;多级索引中的数学选取 首先&#xff0c;导入 NumPy 库和 Pandas 库。 import numpy as np i…

monitor link 联合smart link配合应对复杂的网络

monitor link关键词&#xff1a;上行和下行端口&#xff0c;当上行端口异常&#xff0c;下行端口立即down掉&#xff0c;也就是一种联动机制 如果上行端口里面是smart link方式&#xff0c;则当主从端口都出问题时候&#xff0c;下行端口才会down掉 monitor link 配置步骤 1创…

Vue基础配置、组件通信、自定义指令

基础配置 Vue框架已经集成了webpack配置 小注意点 vbase 快速生成vue模板 组件名必须是多词格式(驼峰模式) 具体三种写法: ①小驼峰:abcDef.vue ②大驼峰&#xff1a;AbcDef.vue ③中横线&#xff1a;abc-def.vue 假如文件名不符合多次格式的补救办法&#xff1a; 导出重命名…

EFPN代码解读

论文 Extended Feature Pyramid Network for Small Object Detection python3 D:/Project/EFPN-detectron2-master/tools/train_net.py --config-file configs/InstanceSegmentation/pointrend_rcnn_R_50_FPN_1x_coco.yaml --num-gpus 1 训练脚本 cfg 中的配置 先获取配置…

基于51单片机的智能门禁系统仿真设计

**单片机设计介绍&#xff0c;基于51单片机的智能门禁系统仿真设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于51单片机的智能门禁系统仿真设计概要 一、设计概述 本设计旨在利用51单片机为核心控制器&#xff0c;结合…

Python程序设计 多重循环(二)

1.打印数字图形 输入n&#xff08;n<9)&#xff0c;输出由数字组成的直角三角图形。例如&#xff0c;输入5&#xff0c;输出图形如下 nint(input("")) #开始 for i in range(1,n1):for j in range(1,i1):print(j,end"")print()#结束 2.打印字符图形 …

pytest教程-22-用例依赖插件-pytest-dependency

领取资料&#xff0c;咨询答疑&#xff0c;请➕wei: June__Go 上一小节我们学习了pytest失败重跑插件pytest-rerunfailures&#xff0c;本小节我们讲解一下pytest用例依赖插件-pytest-dependency。 用例依赖 编写用例的时候&#xff0c;我们会注意用例之间的独立性&#xf…

【电源专题】电池不均衡的影响与原因

在使用多节电池设计产品时,大家都知道如果多节电池不均衡会影响电池寿命与充电安全。特别是在充电末端与放电末端时表现较为明显。 电池不均衡的影响 那么为什么会影响安全与寿命呢?其原因如下: 如果电池不均衡时,相当于木桶的短板效应。一方面没法充满,充电时电压高的那一…

《QT实用小工具·七》CPU内存显示控件

1、概述 源码放在文章末尾 CPU内存显示控件 项目包含的功能如下&#xff1a; 实时显示当前CPU占用率。实时显示内存使用情况。包括共多少内存、已使用多少内存。全平台通用&#xff0c;包括windows、linux、ARM。发出信号通知占用率和内存使用情况等&#xff0c;以便自行显示…