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

这篇具有很好参考价值的文章主要介绍了网络编程详解(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完整交互过程

网络编程详解(select poll epoll reactor),网络,开发语言

2.网络演变过程

2.1 演变的本质

网络编程详解(select poll epoll reactor),网络,开发语言

2.2 阻塞IO:Blocking IO

网络编程详解(select poll epoll reactor),网络,开发语言

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

2.3 非阻塞IO:Nonblocking IO

  • 核心矛盾:之所以一个client连接分配一个线程是因为处理客户端的读写时阻塞式的,为避免该阻塞影响后续接收新的client连接,所以将阻塞逻辑交由单独线程处理
    网络编程详解(select poll epoll reactor),网络,开发语言

  • 非阻塞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时并不知道什么时候数据会准备好,所以需要不断的主动询问
    网络编程详解(select poll epoll reactor),网络,开发语言

  • 所谓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 epoll reactor),网络,开发语言

select 和 poll 的区别

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

优缺点

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

2.5 IO多路复用第二版:epoll

  • 核心矛盾:select/poll每次都需要将管理的多个client从用户态拷贝到内核态,影响性能
    网络编程详解(select poll epoll reactor),网络,开发语言

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的时间(读、写、接收连接等),具体如下:
    网络编程详解(select poll epoll reactor),网络,开发语言
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模式区别

网络编程详解(select poll epoll reactor),网络,开发语言

epoll内核实现

网络编程详解(select poll epoll reactor),网络,开发语言
网络编程详解(select poll epoll reactor),网络,开发语言

2.5 异步IO

  • 异步io,两个阶段都不会被阻塞
    网络编程详解(select poll epoll reactor),网络,开发语言

同步IO和异步IO的区别

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

网络编程详解(select poll epoll reactor),网络,开发语言

3. 主流网络模型

3.1 thread-based架构模型

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

  • 原因:

    • 线程的创建、销毁开销较大
    • 创建的线程需要占用一定的资源
    • 线程切换需要一定的资源开销
    • 一个进程能开辟的线程数据有限
      网络编程详解(select poll epoll reactor),网络,开发语言
  • 对应的是阻塞IO

3.2 single-reactor单线程网络模型

  • 核心:IO中的accept、read、write都是在一个线程完成的
  • 存在问题:目前该模型中,除了IO操作在reactor线程外,业务逻辑处理操作也在reactor线程上,当业务逻辑处理比较耗时时,会大大降低了IO请求的处理效率
  • 典型实现:redis(4.0之前)
    网络编程详解(select poll epoll reactor),网络,开发语言

3.3 single-reactor线程池模型

  • 如何改进:引入了线程池,用来专门处理业务逻辑操作,提升IO响应速度
  • 缺陷:虽然在引入线程池后IO响应速度提升了,但在管理百万级连接、高并发大数据量时,单个reactor线程仍然会效率比较低下
    网络编程详解(select poll epoll reactor),网络,开发语言

3.4 multi-reactor多线程模型

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

网络编程详解(select poll epoll reactor),网络,开发语言

3.5 multi-reactor多进程模型

  • mainreactor进程主要负责接收客户端连接,并将建立的客户端连接进行分发给subreactor进程中
  • subreactor进程主要负责处理客户端的数据读写和业务逻辑的处理
  • 经典实现:nginx
    网络编程详解(select poll epoll reactor),网络,开发语言

两种multi-reactor模型对比

网络编程详解(select poll epoll reactor),网络,开发语言文章来源地址https://www.toymoban.com/news/detail-845653.html

到了这里,关于网络编程详解(select poll epoll reactor)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【高并发网络通信架构】引入IO多路复用(select,poll,epoll)实现高并发tcp服务端

    目录 一,往期文章 二,基本概念 IO多路复用 select 模型 poll 模型 epoll 模型 select,poll,epoll 三者对比 三,函数清单 1.select 方法 2.fd_set 结构体 3.poll 方法 4.struct pollfd 结构体 5.epoll_create 方法 6.epoll_ctl 方法 7.epoll_wait 方法 8.struct epoll_event 结构体 四,代码实现 select 操作流程 s

    2024年02月12日
    浏览(42)
  • 【高并发网络通信架构】3.引入IO多路复用(select,poll,epoll)实现高并发tcp服务端

    目录 一,往期文章 二,基本概念 IO多路复用 select 模型 poll 模型 epoll 模型 select,poll,epoll 三者对比 三,函数清单 1.select 方法 2.fd_set 结构体 3.poll 方法 4.struct pollfd 结构体 5.epoll_create 方法 6.epoll_ctl 方法 7.epoll_wait 方法 8.struct epoll_event 结构体 四,代码实现 select 操作流程 s

    2024年02月14日
    浏览(31)
  • linux poll,epoll,select的区别

    epoll中红黑树的作用? 红黑树(rbtree)、以及epoll的实现原理_epoll 红黑树_For Nine的博客-CSDN博客 红黑树和epoll_wait的关系? epoll_wait/就绪list和红黑树的关系 - 知乎 其他区别: 1. select 在linux内核中限制了能监听的数目上限。32位是1024,64位是2048 2. poll是将监听的对象改成了链表

    2024年02月03日
    浏览(26)
  • IO多路复用之select/poll/epoll

    掌握select编程模型,能够实现select版本的TCP服务器. 掌握poll编程模型,能够实现poll版本的TCP服务器. 掌握epoll的编程模型,能够实现epoll版本的TCP服务器. epoll的LT模式和ET模式. 理解select和epoll的优缺点对比. 提示:以下是本篇文章正文内容,下面案例可供参考 多路转接天然的是让我

    2023年04月09日
    浏览(60)
  • 多路转接方案:select poll epoll 介绍和对比

    内存和外设的交互叫做IO,网络IO就是将数据在内存和网卡间拷贝。 IO本质就是等待和拷贝,一般等待耗时往往远高于拷贝耗时。所以提高IO效率就是尽可能减少等待时间的比重。 IO模型 简单对比解释 阻塞IO 阻塞等待数据到来 非阻塞IO 轮询等待数据到来 信号驱动 信号递达时

    2024年02月08日
    浏览(37)
  • select,poll,epoll阻塞IO使用示例介绍

    epoll 打开设备文件或套接字,并确保设备或套接字处于可读或可写状态。 创建一个 epoll 实例,使用 epoll_create 函数创建一个 epoll 文件描述符。 将设备文件或套接字的文件描述符添加到 epoll 实例中,使用 epoll_ctl 函数将设备文件或套接字的文件描述符添加到 epoll 实例中,并设

    2024年02月12日
    浏览(30)
  • Day 9. TCP并发模型、select、poll、epoll

    缺点: 1)创建线程会带来资源开销,能够实现 1)阻塞IO:没有数据到来时,可以让任务故挂起,节省CPU资源开销,提高系统效率 2)非阻塞IO:程序未接受到数据时程序一直执行,效率很低 3)异步IO:只能绑定一个文件描述符用来读取数据,但是效率很高 4)多路复用IO:

    2024年04月10日
    浏览(31)
  • 【Linux】高级IO --- 多路转接,select,poll,epoll

    所有通过捷径所获取的快乐,无论是金钱、性还是名望,最终都会给自己带来痛苦 1. 后端服务器最常用的网络IO设计模式其实就是Reactor,也称为反应堆模式,Reactor是单进程,单线程的,但他能够处理多客户端向服务器发起的网络IO请求,正因为他是单执行流,所以他的成本就

    2024年02月09日
    浏览(46)
  • 网络编程 IO多路复用 [epoll版] (TCP网络聊天室)

    //head.h            头文件 //TcpGrpSer.c     服务器端 //TcpGrpUsr.c     客户端 通过IO多路复用实现服务器在单进程单线程下可以与多个客户端交互  API epoll函数  head.h TcpGrpSer.c TcpGrpUsr.c  

    2024年02月11日
    浏览(43)
  • TCP高并发服务器简介(select、poll、epoll实现与区别)

    一、创建套接字(socket函数): 二、填充服务器的网络信息结构体: 三、套接字和服务器的网络信息结构体进行绑定(bind函数): 四、套接字设置成被动监听(listen函数): 五、创建要监听的文件描述符集合: 使用select函数后,会将 没有就绪的文件描述符 在集合中 去除

    2024年01月19日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包