您的位置:首页 > 房产 > 家装 > 《TCP/IP网络编程》(第十二章)I/O复用(1)

《TCP/IP网络编程》(第十二章)I/O复用(1)

2024/12/23 11:04:14 来源:https://blog.csdn.net/m0_53115174/article/details/139278620  浏览:    关键词:《TCP/IP网络编程》(第十二章)I/O复用(1)

本章将讨论实现并发服务器的第二种办法,基于I/O复用的服务器端构建

I/O复用它允许单个进程或线程同时处理多个输入/输出(I/O)操作,而无需为每个I/O操作创建一个独立的线程或进程。这种技术可以显著提高应用程序的效率和性能,特别是在需要处理大量并发连接的场景下。

1.多进程服务器端的缺点

基于进程的并发服务器在创建进程时,需要付出极大的代价,因为需要大量的运算和内存空间,而且由于每个进程具有独立的内存空间,相互间交换数据的方法也很复杂。
如果使用I/O复用技术,则可以在不创建进程的情况下同时向多个客户端进行服务。

2.基于I/O复用的服务器端

在这里插入图片描述
上图左边时多进程服务器端模型,右边是I/O复用服务器端模型,可以看出后者无论连接多少个客户端,提供服务的进程只有1个

3.理解select()函数

select() 函数是一种用于I/O复用的系统调用,它允许程序同时监视多个文件描述符(包括套接字),以确定哪些文件描述符已经准备好进行读取或写入操作。select() 函数在Linux系统和Windows系统中都支持,下面是以Linux系统为例,Windows系统示例放在最后。

select()函数基本语法

//发生错误时返回-1; 
//发生关注的事件返回时,返回该事件的文件描述符(大于0)
int select(
int maxds, //设置位监视的文件描述符中最大的描述符加1。例如,如果你监视的文件描述符范围是0到5,则maxfds应该设置为6。
fd_set *readfds, //将所有关注“是否有可读数据”的文件描述符注册到fd_set,并传递其地址值
fd_set *writefds,//将所有关注“是否有可写数据”的文件描述符注册到fd_set,并传递其地址值
fd_set *exceptfds, //将所有关注“是否发生异常”的文件描述符注册到fd_set,并传递其地址值
struct timeval *timeout//设置select() 调用的超时时间,为了防止调用select()函数后,陷入无限阻塞
);//struct timeval的结构体定义
struct timeval{
long tv_sec;    //秒数
long tv_usec;   //毫秒数
}

select()函数的调用过程

select()函数的调用流程如下图所示,本文将逐步介绍

在这里插入图片描述

  1. 设置文件描述符: select()可以监视多个文件描述符,将需要监视的文件描述符集中为一个集合fd_set,集中时也要按照读、写、异常进行分为三类,fd_set结构如下图所示:
    在这里插入图片描述
    设置为1则代表时监视对象,所以上图中fd1和fd3的是监视对象。在fd_set变量中用下列宏进行操作:
FD_ZERO(fd_set* fdset);          //将所有fd_set变量的所有位设置位0
FD_SET(int fd, fd_set* fdset);   //将fd_set中指定变量的位设置为1
FD_CLR(int fd, fd_set* fdset);   //将fd_set中指定变量的位设置为0
FD_ISSET(int fd, fd_set* fdset); //如果文件描述符在集合中,返回非零值;如果不在集合中,返回0。

在这里插入图片描述

  1. 指定监视范围:select() 函数中的第一个参数maxds, 要监视的文件描述符中最大的描述符加1。例如,如果你监视的文件描述符范围是0到5,则maxfds应该设置为6,因为文件描述分值是从0开始

  2. 设置超时: 如果监视的文件描述符一直没有变化,则会陷入阻塞状态,所以需要设置超时时间,告知服务器超时消息。将秒数填入tv_sec,将毫秒数填入tv_usec

  3. 调用select()函数: 调用 select() 函数,函数会阻塞,直到有文件描述符发生变化或者超时。

  4. 查看调用结果: 返回-1则发生错误;返回一个整数值,则代表有多少个文件描述符准备好了I/O操作,可以进行读取操作了,下图是变化
    在这里插入图片描述
    调用函数前,告诉select()函数,需要监视fd1、fd2、fd3,当调用函数后,首先是将监视的文件描述符的位初始化为0,然后监视到fd1和fd3发生变化,所以fd1和fd3的位又变成了1。

③select()函数示例

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/select.h>int main(){fd_set reads,temps;// 定义文件描述符集合,reads用于存储关注是否可读的文件描述符,temps用于存储select函数返回时准备好的文件描述符int result,str_len;// result用于存储select函数的返回值,str_len用于存储读取的字节数char buff[1024];struct timeval timeout;// 定义一个timeval结构,用于设置select函数的超时时间FD_ZERO(&reads);// 清空reads集合FD_SET(0,&reads);// 将文件描述符为0的设置为监视状态,文件描述符0通常代表标准输入(stdin),它是一个特殊的文件描述符,用于从键盘接收输入。while (1){temps=reads;/// 复制reads集合到temps集合timeout.tv_sec=5;timeout.tv_usec=0;result=select(1,&temps,0,0,&timeout);//1表示有1个文件被监视,temps表示被监视“是否可读”的文件描述符集合,0表示“不关注可写”文件描述符,0表示“不关注异常”文件描述符,timeout表示超时时间if(result==-1){// 如果select函数返回-1,表示出错puts("select error");break;}else if(result==0){// 如果select函数返回0,表示超时puts("time-out");}else{// 如果select函数返回大于0,表示有文件描述符准备好了if(FD_ISSET(0,&temps)){// 检查文件描述符0是否在temps集合中str_len=read(0,buff,1024);// 从文件描述符0读取数据到buff缓冲区,最多1024字节buff[str_len]=0;printf("message from console: %s",buff);}}}return 0;}

在这里插入图片描述

键盘输入有被正常监视到,如果不输入,5秒后会输出超时信息

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com