Linux——I/O复用(select的用法)

news2024/12/1 0:33:58

一、I/O复用

定义:I/O 复用使得程序能同时监听多个文件描述符,这对于提高程序的性能至关重要。

网络程序在下列情况下需要使用 I/O 复用技术:

  • ◼ TCP 服务器同时要处理监听套接字和连接套接字。
  • ◼ 服务器要同时处理 TCP 请求和 UDP 请求。
  • ◼ 程序要同时处理多个套接字。
  • ◼ 客户端程序要同时处理用户输入和网络连接。
  • ◼ 服务器要同时监听多个端口。

  •     需要指出的是,I/O 复用虽然能同时监听多个文件描述符,但它本身是阻塞的。并且当 多个文件描述符同时就绪时,如果不采取额外的措施,程序就只能按顺序依处理其中的每一 个文件描述符,这使得服务器看起来好像是串行工作的。如果要提高并发处理的能力,可以 配合使用多线程或多进程等编程方法。

二、I/O复用的系统调用

I/O复用方法的功能:检查若干个描述符有没有关系的事件产生。

1、select

(1)在一段指定时间内,监听用户感兴趣的文件描述符上的可读、可写和异常等事件。

(2)select参数

①nfds是指定被监听的文件描述符的总数目;

   fd_set 是结构指针类型:

②readfds指向可读事件对应的文件描述符集合;

③writefds是指向可写事件对应的文件描述符集合;

④exceptfds是指向异常事件对应的文件描述符集合。

fd_set结构体定义:

⑤timeout是用来设置select函数的超时时间。它是一个timeval结构类型的指针,采用指针参数是因为内核将修改它,用来告诉应用程序select等待了多久。

(3)sclect成功时返回就绪(可读、可写和异常)文件描述符的总数。

  • 如果在超时时间内没有任何文件描述符就绪,select将返回0。select失败时返回-1并设置errno。
  • 如果在select等待期间,程序接收到信号,则select立即返回-1,并设置errno为 EINTR。

2、文件描述符就绪条件

(1)在网络编程中,以下情况socket可读

  • ①socket内核接收缓存区中的字节数大于或等于其低水位标记SO RCVLOWAT。此时我们可以无阻塞地读该socket,并且读操作返回的字节数大于0。
  • ②socket通信的对方关闭连接。此时对该socket的读操作将返回0。监听socket上有新的连接请求。
  • ③socket 上有未处理的错误。此时我们可以使用getsockopt来读取和清除该错误。

(2)在网络编程中,以下情况socket可写

  • ①socket内核发迭缓存区中的可用字节数大于或等于其低水位标记so_SNDLOWAT。此时我们可以无阻塞地写该socket,并且写操作返回的字节数大于0。
  • ②socket的写操作被关闭。对写操作被关闭的socket执行写操作将触发一个SIGPIPE 信号。
  • ③socket使用非阻塞connect连接成功或者失败(超时)-之后。
  • ④socket上有未处理的错误。此时我们可以使用getsockopt来读取和清除该错误。
  • 注意:select能处理的异常情况只有一种:socket上接收到带外数据。

3、select的用法示例

//select用法
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/select.h>
#include<sys/time.h>


#define STDIN 0

int main()
{
    int fd=STDIN;//键盘对应的描述符
    fd_set fdset;//收集描述符
    
    while(1)
    {
        FD_ZERO(&fdset);//清空集合
        FD_SET(fd,&fdset);//添加描述符(fd)到集合
        struct timeval tv={5,0};
        int n=select(fd+1,&fdset,NULL,NULL,&tv);
        if(n==-1)
        {
            printf("select err\n");//退出
        }
        else if(n==0)
        {
            printf("time out\n");//超时
        }
        else
        {
            //检测描述符上是否有数据,如果为真,就是有数据
            if(FD_ISSET(fd,&fdset))//按位与,同1为1
            {
                char buff[128]={0};
                read(fd,buff,127);
                printf("buff=%s\n",buff);
            }
        }  
    }      
}

4、使用select实现的TCP服务 

//tcp服务器端
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/select.h>
#include<sys/time.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>


#define MAXFD 10

void fds_init(int fds[])
{
    for(int i=0;i<MAXFD;i++)
    {
        fds[i]=-1;
    }
}

//把描述符添加到数组中
void fds_add(int fd,int fds[])
{
    for(int i=0;i<MAXFD;i++)
    {
        if(fds[i]==-1)//说明是空的,该元素是空闲的
        {
            fds[i]=fd;//把对应的元素置成fd
            break;
        }
    }
}

//移除一个描述符
void fds_del(int fd,int fds[])
{
    for(int i=0;i<MAXFD;i++)
    {
        if(fds[i]==fd)
        {
            fds[i]==-1;//说明移除成功
            break;
        }
    }
}

int accept_client(int sockfd)
{
    struct sockaddr_in caddr;
    int len=sizeof(caddr);
    int c=accept(sockfd,(struct sockaddr*)&caddr,&len);
    return c;
}

int socket_init();

int main()
{
    int fds[MAXFD];//记录(收集)描述符的数组
    fds_init(fds);
    
    int sockfd=socket_init();//监听套接字
    if(sockfd==-1)
    {
        exit(0);
    }
    
    fds_add(sockfd,fds);//添加了一个数组
    
    fd_set fdset;//集合收集描述符
    
    while(1)
    {
        FD_ZERO(&fdset);
        int maxfd=-1;
    
        for(int i=0;i<MAXFD;i++)
        {
            if(fds[i]==-1)
            {
                continue;
            }
            FD_SET(fds[i],&fdset);//将数组中有效的描述符,添加到集合中
            if(fds[i]>maxfd)//找到数组中描述符的最大值
            {
                maxfd=fds[i];
            }
        }
   
        struct timeval tv={5,0};

        int n=select(maxfd+1,&fdset,NULL,NULL,&tv);//阻塞五秒钟
        if(n==-1)
        {
            printf("select err\n");
        }
        else if(n==0)
        {
            printf("time out\n");
        }
        else
        {
            for(int i=0;i<MAXFD;i++)
            {
                if(fds[i]==-1)
                {
                    continue;
                }
                if(FD_ISSET(fds[i],&fdset))
                {
                    if(fds[i]==sockfd)//监听套接字,accept
                    {
                        int c=accept_client(fds[i]);
                        if(c!=-1)
                        {
                            fds_add(c,fds);//添加新接收的连接
                        }
                    }
                    else//连接套接字,recv
                    {
                        char buff[128]={0};
                        int num=recv(fds[i],buff,127,0);
                        if(num<=0)
                        {
                            close(fds[i]);
                            fds_del(fds[i],fds);
                        }
                        else
                        {
                            printf("recv:&s\n",buff);
                            send(fds[i],"ok",2,0);
                        }
                    }
                }
            }
        }
    
    }
    
}
    
int socket_init()
{
    int sockfd=socket(AF_INET,SOCK_STREAM,0);
    if(sockfd==-1)
    {
        return -1;
    }
    
    struct sockaddr_in saddr;
    memset(&saddr,0,sizeof(saddr));
    saddr.sin_family=AF_INET;
    saddr.sin_port= htons(6000);//大端端口,tcp
    saddr.sin_addr.s_addr=inet_addr("127.0.0.1");//环回地址
    int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
    if(res==-1)
    {
        printf("bind err\n");
        return -1;
    }
    
    res=listen(sockfd,5);
    if(res==-1)
    {
        return -1;
    }
    
    return sockfd;
    
}
//客户端cli
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>

int main()
{
    int sockfd=socket(AF_INET,SOCK_STREAM,0);
    if(sockfd==-1)
    {
        printf("socket err\n");
        exit(0);
    }

    struct sockaddr_in saddr;
    memset(&saddr,0,sizeof(saddr));
    saddr.sin_family=AF_INET;
    saddr.sin_port=htons(6000);//1024以内是专用;我们用4096以上,root 
    saddr.sin_addr.s_addr=inet_addr("127.0.0.1");

    int res=connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));//三次握手开始
    if(res==-1)
    {
        printf("connect failed\n");
        exit(0);
    }

    //从键盘获取数据
    while(1)
    {
        printf("input: \n");
        char buff[128]={0};
        fgets(buff,128,stdin);
        if(strncmp(buff,"end",3)==0)
        {
            break;
         }

        send(sockfd,buff,strlen(buff)-1,0);
        memset(buff,0,128);//清空
        int n=recv(sockfd,buff,127,0);
        if(n<=0)//判断服务器是否关闭
        {
            break;
        }
        printf("buff=%s\n",buff);
    }

    close(sockfd);

}

如有错误,敬请指正。

您的收藏与点赞都是对我最大的鼓励和支持!

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

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

相关文章

RT-Thread的设备模型

RTT内核对象——设备 RT-Thread有多种内核对象&#xff0c;其中设备device就是其中一种。 内核继承关系图如下&#xff1a; 设备继承关系图如下&#xff1a; I/O 设备模型框架 应用程序通过 I/O 设备管理接口获得正确的设备驱动&#xff0c;然后通过这个设备驱动与底层 I/O 硬…

ARM通用中断控制器GIC之中断处理状态机 Interrupt handling state machine

中断有四种状态&#xff1a;inactive&#xff0c;pending&#xff0c;active 和active and pending。而产生中断的方式有两种&#xff0c;一种是通过写pending寄存器&#xff0c;让中断进入pending状态&#xff0c;可以忽略是否真的有物理中断信号&#xff0c;让Distributor将该…

如何构建myquant量化策略?

对于如何构建myquant量化策略这个问题而言&#xff0c;就是获取量化股票接口的基础数据&#xff0c;然后有了基础数据&#xff0c;才能对数据进行加工处理&#xff0c;构建量化策略&#xff0c;进行量化分析&#xff0c;回测和回溯。myquant量化策略主要是基于python进行量化投…

案例实操 | 利用Lambda函数来进行特征工程,超方便的!!

特征工程对于我们在机器学习的建模当中扮演着至关重要的角色&#xff0c;要是这一环节做得好&#xff0c;模型的准确率以及性能就被大大地被提升&#xff0c;今天小编就通过Python当中的lambda函数来对数据集进行一次特征工程的操作&#xff0c;生成一些有用的有价值的特征出来…

nacos--基础--3.4--集成--spring--spring上下文中的一些关键的特性

nacos–基础–3.4–集成–spring–spring上下文中的一些关键的特性 1、spring上下文中的一些关键的特性 注解驱动依赖注入外部化配置事件驱动 2、注解驱动 2.1、 启用 Nacos EnableNacos 是一个模块驱动的注解EnableNacos 支持 Nacos Spring 的所有功能 服务发现&#xff1…

香港服务器托管带宽怎么选?

香港服务器托管时&#xff0c;带宽的选择非常重要&#xff0c;带宽用量的大小不同&#xff0c;最终的费用也就会不同。用户在香港托管服务器的时候&#xff0c;应该选择多大的带宽才合适呢?应该如何选择香港服务器带宽大小呢? 香港服务器托管带宽怎么选? 其实&#xff0c;大…

5G无线技术基础自学系列 | 无线电波传播模型

素材来源&#xff1a;《5G无线网络规划与优化》 一边学习一边整理内容&#xff0c;并与大家分享&#xff0c;侵权即删&#xff0c;谢谢支持&#xff01; 附上汇总贴&#xff1a;5G无线技术基础自学系列 | 汇总_COCOgsta的博客-CSDN博客 无线电波传播模型用于预测无线电波在各…

JAVA开发(JWTUtil工具类实现token源码赏析)

一、token解决问题的背景&#xff1a; 1、 单点登录&#xff0c; 2、分布式登录状态&#xff0c; 3、在信任期内 避免 重新输入用户名密码登录。 4、跨系统免登陆等。 5、一些接口的请求权限验证。 总结以上token最终都是为了解决在在分布式系统中信任期内 避免 重新输入用户名…

如何选择合适的统计学方法

一般来说&#xff0c;使用哪种统计方法&#xff0c;取决于我们的应用场景、我们的研究目的是什么。 这里面一个麻烦的地方在于&#xff0c;不管你使用哪种统计方法&#xff0c;似乎都可以得出一个’结果’或一个p值&#xff0c;但这个结果有没有意义就两说了。 在我看来&…

【WEB安全】Xstream最新反序列化poc执行报错问题

前言 最近有个需求&#xff0c;用Xstream反序列化打个内存马&#xff0c;从通用性来讲&#xff0c;肯定用1.4.17的洞去打应用范围最广。众所周知&#xff0c;Xstream官方会提供其漏洞的poc。在我实验之下&#xff0c;1.4.17的几个poc只要涉及到任意java代码执行的都会报错&…

VMware创建共享文件夹并实现文件传输(Windows主机,Ubuntu虚拟机)

文章目录创建共享文件夹安装/更新VMware tool挂载共享文件夹共享文件夹的实现更改配置项&#xff0c;实现自动挂载&#xff08;推荐直接用这个&#xff09;数据传输参考博客创建共享文件夹 1、开启虚拟机的共享文件夹配置 首先在虚拟机中打开设置&#xff0c;在选项中点击共享…

Java springboot+vue+MySQLJava大型CRM客户关系管理源码带手机端和小程序源码 Java客户全流程高效管理CRM系统源码

springbootvue MySQL数据库 1. 前端&#xff1a;Vue 2. 后端&#xff1a;Spring boot 3. 数据库&#xff1a;MySQL 源码已经亲测 1. 前端&#xff1a;Vue 2. 后端&#xff1a;Spring boot 3. 数据库&#xff1a;MySQL 小程序是用UNIAPP开发的。 后台登录账户&#xff1a…

(附源码)springboot智能服药提醒app 毕业设计 102151

基于springboot智能服药提醒app 摘 要 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;智能服药提醒app程序被用户…

平均价格算法:TWAP vs. VWAP

时间加权平均价格 (TWAP) 和成交量加权平均价格 (VWAP) 算法应用不同的方法来计算资产价格&#xff0c;这是所有去中心化金融 (DeFi) 原语的组成部分。 在本文中&#xff0c;我们介绍了 TWAP 和 VWAP 算法之间的差异&#xff0c;解释了它们如何在区块链环境中为资产定价&#…

化学工程与装备杂志化学工程与装备杂志社化学工程与装备编辑部2022年第10期目录

科学研究与开发《化学工程与装备》投稿&#xff1a;cnqikantg126.com 槐木活性炭处理生活废水的研究 孔繁铸;高丽娟;赵开创;钱程; 1-3 基于Pumplinx的水泵机械密封温升仿真研究 赵雪飞;贾红杰;李雪峰;李雷;王景新;张晓辉; 4-728 全固态石墨烯-Ag/AgCl海洋电场探测电…

功率放大器的输入阻抗和输出阻抗是交流还是直流电阻

功率放大器的输入阻抗和输出阻抗一般是指交流电阻。交流电阻指一些伏安特性曲线的斜率。所以放大器输入阻抗和输出阻抗都是理论分析计算为主&#xff0c;而不使用万用表测量其大小。 输入阻抗一般是指电路输入端的等效阻抗&#xff0c;输入端加上一个电压源U&#xff0c;测量输…

WRF模式安装+详细运行教程

安装教程在安装之前&#xff0c;首先需要去查看自己的 Linux 系统&#xff08;虚拟机或者服 务器&#xff09;上的编译环境&#xff0c;也就是确定自己是否安装编译器或安装了哪种编 译器。&#xff08;因为安装过程需要选择编译器&#xff09;常用的编译器分为三种系列&#x…

Typora基础使用技巧

Typora Tutorial 1.数学符号及公式 (1).常用数学符号 数学符号没什么特别的语法&#xff0c;这里顺道总结以下方便使用&#xff1a; &#xff0b; &#xff0d; ≠ ≈ / &#xff1e; &#xff1c; ≯ ≮ ≤ ≥ ∵ ∴ ∽ ≌&#xff0c; √&#xff0c; Φ Χ Ψ Ωα…

antd-protable的分页逻辑封装

一、前言 这次的分页基于pro-table进行封装。 前端分页看似是个小功能&#xff0c;但里面隐藏的逻辑还是非常多的&#xff0c;比如我们业务中需要的分页器除了能进行正常的页码切换等操作外&#xff0c;还需要如下的功能&#xff1a; 次页删除最后一条数据需要跳转至第一页&…

计算机研究生就业方向之去大厂做大数据

我一直跟学生们说你考计算机的研究生之前一定要想好你想干什么&#xff0c;如果你只是转码&#xff0c;那么你不一定要考研&#xff0c;至少以下几个职位研究生是没有啥优势的&#xff1a; 1&#xff0c;软件测试工程师&#xff08;培训一下就行&#xff09; 2&#xff0c;前…