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

news2024/12/28 3:11:15

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.coloradmin.cn/o/1564050.html

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

相关文章

【保姆级讲解下MySQL中的drop、truncate和delete的区别】

&#x1f308;个人主页:程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

4.2学习总结

一.java学习总结 (本次java学习总结,主要总结了抽象类和接口的一些知识,和它们之间的联系和区别) 一.抽象类 1.1定义: 抽象类主要用来抽取子类的通用特性&#xff0c;作为子类的模板&#xff0c;它不能被实例化&#xff0c;只能被用作为子类的超类。 2.概括: 有方法声明&…

在jsp文件内使用jdbc报错

使用idea创建javaweb项目后&#xff0c;在jsp文件内使用jdbc连接数据库错误&#xff0c;显示以下内容&#xff1a; java.lang.ClassNotFoundException: com.microsoft.sqlserver.jdbc.SQLServerDriverat org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappCl…

相关滤波跟踪算法-CSK

0. 写在前面 对相关滤波算法综述比较强的文档&#xff1a; NIUBILITY的相关滤波框架详解 - 知乎 (zhihu.com) 1. 概述 相关滤波算法问世之前&#xff0c;跟踪算法饱受运行时间的困扰&#xff0c;直到MOSSE算法出现&#xff0c;直接将算法速度提到了615fps&#xff0c;第一次将…

Makefile:通用部分头文件与条件判断(八)

1、通用部分做头文件 首先举个例子看看为什么需要这个东西&#xff0c;例如在一个文件夹下有两个项目&#xff0c;两个项目都需要编写makefile编译&#xff0c;此时可以使用公共头文件 目录结构如下&#xff1a; 1.1、项目&#xff08;一&#xff09; 有a.cpp、b.cpp、c.cpp…

虚拟机安装银河麒麟

背景 由于Centos将于2024-06-30结束维护【脱保】&#xff0c;届时会存在Bug无人修复及功能无人开发等问题&#xff0c;所以要赶在这个节点前完成操作系统升级。可选的就是RedHat、Ubuntu以及国产信创【中标麒麟、银河麒麟、统信等】&#xff0c;或者使用云上操作系统【例如租阿…

嵌入式4-2

今日作业&#xff1a;使用文件IO 实现父进程向子进程发送信息&#xff0c;并总结中间可能出现的各种问题 #include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> #include <fcntl.h> #include <unistd.h> int m…

C#中值类型与引用类型的存储

目录 值对象与引用对象的存储 引用对象的成员存储 值对象与引用对象的存储 数据项的类型定义了存储数据需要的内存大小及组成该类型的数据成员。类型还决定了对象在内存中的存储位置——栈或堆。 C#中类型分为两种&#xff1a;值类型和引用类型&#xff0c;这两种类型的对象…

YOLOv8全网独家改进: 小目标 | 注意力 |卷积和注意力融合模块(CAFMAttention) | 2024年4月最新成果

💡💡💡本文独家改进:卷积和注意力融合模块(CAFMAttention),增强对全局和局部特征的提取能力,2024年最新的改进思路 💡💡💡创新点:卷积和注意力巧妙设计 💡💡💡如何跟YOLOv8结合:1)放在backbone后增强对全局和局部特征的提取能力;2)放在detect前面,增…

公司只有一个测试,要怎么继续呆下去?

在面试的时候&#xff0c;面试官可能会问&#xff1a;小公司、小团队&#xff0c;岗位就你一个人&#xff0c;怎么做 &#xff1f; 或者已经有的小伙伴已经在公司中面临只有一个测试的处境&#xff0c;这个时候我们应该怎么处理呢&#xff1f; 一 原因分析 公司只有一个测试人…

网易云首页单页面html+css

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

1999-2022年上市公司员工人数数据

1999-2022年上市公司员工人数数据 1、时间&#xff1a;1999-2022年 2、指标&#xff1a;证券代码、时间、员工人数 3、来源&#xff1a;整理自csmar 4、范围&#xff1a;上市公司 5、指标解释&#xff1a; 上市公司员工人数是衡量公司规模和发展状的重要指标。该数据直接…

4.2 JavaWeb Day05分层解耦

三层架构功能 controller层接收请求&#xff0c;响应数据&#xff0c;层内调用了service层的方法&#xff0c;service层仅负责业务逻辑处理&#xff0c;其中要获取数据&#xff0c;就要去调用dao层&#xff0c;由dao层进行数据访问操作去查询数据&#xff08;进行增删改查&…

Vision Pro开发实践(一)

简介 Vision Pro是苹果公司的首款头戴式“空间计算”显示设备&#xff0c;于2023年6月6日在“WWDC2023”正式发布&#xff0c;同时推出的还有专为Vision Pro打造的操作系统平台visionOS&#xff0c;以及一整套“新的”开发工具&#xff0c;之所以打引号&#xff0c;是因为用于…

Taro多行文本最多展示5行,超出“查看更多”展示,点击弹层

Taro中&#xff0c;页面需求&#xff1a; 多行文本&#xff0c;展示最多展示5行&#xff0c;超出5行&#xff0c;展示“查看更多”按钮&#xff0c;点击弹层展示文本详细信息。 弹层代码就不说了&#xff0c;着重说一下怎么获取区域高度&#xff5e; 1.区域设置max-height&am…

【AI绘画/作图】风景背景类关键词模板参考

因为ds官网被墙,所以翻了IDE的源码整理了下stablestudio里的官方模板&#xff0c;顺便每个模板生成了一份…不知道怎么写关键词的可以参考 Stunning sunset over a futuristic city, with towering skyscrapers and flying vehicles, golden hour lighting and dramatic cloud…

java的警示之有危险的行为

&#x1f468;‍&#x1f4bb;作者简介&#xff1a;&#x1f468;&#x1f3fb;‍&#x1f393;告别&#xff0c;今天 &#x1f4d4;高质量专栏 &#xff1a;☕java趣味之旅 欢迎&#x1f64f;点赞&#x1f5e3;️评论&#x1f4e5;收藏&#x1f493;关注 &#x1f496;衷心的希…

ssm016基于 Java Web 的校园驿站管理系统+jsp

校园驿站管理系统的设计与实现 摘 要 互联网发展至今&#xff0c;无论是其理论还是技术都已经成熟&#xff0c;而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播&#xff0c;搭配信息管理工具可以很好地为人们提供服务。针对校园快递信息管理混乱&#xff0c;出…

类和对象的下篇

&#x1d649;&#x1d65e;&#x1d658;&#x1d65a;!!&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦ &#x1f44f;&#x1f3fb;‧✧̣̥̇:Solitary_walk ⸝⋆ ━━━┓ - 个性标签 - &#xff1a;来于“云”的“羽球人”。…

从零实现一个Http服务器

HttpServer HTTPServer项目是一个基于C编写的简单的HTTP服务器实现&#xff0c;用于处理客户端的HTTP请求并提供相应的服务。该项目使用了Socket编程来实现服务器与客户端之间的通信&#xff0c;通过监听指定的端口并接受客户端连接&#xff0c;然后解析HTTP请求并生成对应的H…