Linux I/O复用函数的使用情况和select接口的介绍

I/O 复用使得程序能同时监听多个文件描述符,这对于提高程序的性能至关重要。通常,
网络程序在下列情况下需要使用 I/O 复用技术:
1.TCP服务器同时要处理监听套接字和连接套接字

2.服务器同时要处理TCP请求和UDP请求。

3.程序同时要处理多个套接字。

4.客服端程序要同时处理用户输入和网络连接。

5.服务器要同时监听多个端口。

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

1select

1.1select的接口介绍

        select系统调用的用途是:再一段指定时间内,监听用户感兴趣的文件描述符的可读、可写和异常事件。

        select系统调用的原型如下:

# include <sys/select.h>
int select (int maxfd,fd_set*readfds,fd_set*writefds,fd_set*exceptfds,struct timeval*timeout);
/*
select 成功时返回就绪(可读、可写和异常)文件描述符的总数。如果在超时间内没有任何文件描述符就绪,select将返回0.select失败返回-1,如果是在select等待期间,程序接收到信号,则select立即返回-1,并设置errno为EINTR。
maxfd 参数指定的被监听的文件描述符的总数。它通常被设置为 select 监听的所
有文件描述符中的最大值+1
readfds、 writefds 和 exceptfds 参数分别指向可读、可写和异常等事件对应的文件
描述符集合。应用程序调用 select 函数时,通过这 3 个参数传入自己感兴趣的文件
描述符。 select 返回时,内核将修改它们来通知应用程序哪些文件描述符已经就绪
通过下列宏可以访问 fd_set 结构中的位:
FD_ZERO(fd_set *fdset); // 清除 fdset 的所有位
FD_SET(int fd, fd_set *fdset); // 设置 fdset 的位 fd,fd添加到集合(置一)
FD_CLR(int fd, fd_set *fdset); // 清除 fdset 的位 fd
int FD_ISSET(int fd, fd_set *fdset);// 测试 fdset 的位 fd 是否被设置
timeout 参数用来设置 select 函数的超时时间。它是一个 timeval 结构类型的指
针,采用指针参数是因为内核将修改它以告诉应用程序 select 等待了多久。 timeval
结构的定义如下
struct timeval
{
    long tv_sec;//秒数
    long tv_usec://微秒数
};
如果给 timeout 的两个成员都是 0,则 select 将立即返回。如果 timeout 传递
NULL,则 select 将一直阻塞,直到某个文件描述符就绪
*/

         

 1.2select的示例代码

使用select实现的TCP服务器如下:

初始化服务器端的sockfd套接字

int initsocket()
{
    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);
    saddr.sin_addr.s_addr=inet_addr("127.0.0.1");
    int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof (saddr));
    if (res==-1)
        return -1;
    res=listen(sockfd,5);
    if (res==-1)
        return -1;
    return sockfd;
}

初始化记录服务器套接字的数组

void initFds(int fds[],int n)
{
    int i=0;
    for (;i<n;i++)
    {
        fds[i]=-1;
    }
}

将套接字描述符添加到数组中

void AddFdToFds(int fds[],int fd,int n)
{   
    int i=0;
    for (;i<n;i++)
    {
        if (fds[i]==-1)
        {
            fds[i]=fd;
            break;
        }
    }
}

删除数组中的套接字描述符

void DelFromFds(int fds[],int fd,int n)
{
    for (int i=0;i<n;i++)
    {
        if (fds[i]==fd)
        {
            fds[i]=-1;
            break;
        }
    }
}

将数组中的套接字描述符设置到fd_set变量中,并返回当前最大文件描述符

int SetFsToFdset(fd_set*fdset,int fds[],int n)
{
    FD_ZERO(fdest);//fdest置零
    int i=0;
    int maxfd=fds[0];//最大文件描述符的值
    for (;i<n;i++)
    {
        if (fds[i]!=-1)
        {
            FD_SET(fds[i],fdest);//将fdest的位fds[i]的值置为1
            if (fds[i]>maxfd)
            {
                maxfd=fds[i];
            }
        }
    }
    return maxfd;
}

服务器和客户端的连接

void GetClientLink(int sockfd,int fds[],int n)
{    
    struct sockaddr_in caddr;
    int len=sizeof (caddr);
    memset(&caddr,0,sizeof(caddr));
    int c=accept(sockfd,(struct sockaddr*)&caddr,&len);
    if (c<0)
    {
        return;
    }
    AddFdToFds(fds,c,n);
}    

处理客户端数据

void DealClientData(int fds[],int n,int clifd)
{
    char data[128];
    int num=recv(clifd,data,127,0);
    if (num<=0)
    {
        DelFdFromFds(fds,clifd,n);
        close(clifd);
    }
    else
    {
        printf ("%d:%s\n",clifd,data);
        send(clifd,"ok",2,0);
    }
}

处理select返回的就绪事件

void DealReadyEvent(int fds[],int n,fd_set*fdset,int sockfd)
{
    int i=0;
    for (;i<n;i++)
    {
        if (fds[i]!=-1&&FD_ISSET(fds[i],fdest))
        {
            if(fds[i]==sockfd)
            {
                GetClientLink(sockfd,fds,n);
            }
            else
            {
                DealClientLink(fds,n,fds[i]);
            }
        }
    }
}

主函数

int main()
{
    int sockfd=initsocket();
    assert(sockfd!=-1);
    fd_set readfds;
    int fds[1024];
    initFds(fds,1024);
    AddFdToFds(fds,sockfd,1024);
    while(1)
    {
        int maxfd=SetFdToFdset(&readfds,fds,MAX_FD);
        struct timeval timeout;
        timeout.tv_sec=2;//秒数
        timeout.tv_usec=0;//微秒数
        int n=select(maxfd+1,&readfds,NULL,NULL,&timeout);
        if (n<0)
        {
            printf ("select error\n"); 
            break;   
        }
        else if (n==0)
        {
            printf ("timeout\n");
            continue;
        }
        DealReadyEvent(fds, 1024, &readfds, sockfd);
    }
    exit(0);
}

TCP 的客户端代码如下:
 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
int initsocket()
{
    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);
    saddr.sin_addr.s_addr=inet_addr("127.0.0.1");
    int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof (saddr));
    if (res==-1)
        return -1;
    res=listen(sockfd,5);
    if (res==-1)
        return -1;
    return sockfd;
}
int main ()
{
    int sockfd=initsocket();
    assert(sockfd!=-1);
    while(1)
    {
        printf ("please input:");
        char buff[128]={0};
        fgets(buff,127,stdin);
        if (strncmp(buff,"bye",3))
        {
            break;
        }
        int n=send(sockfd,buff,strlen(buff)-1,0);
        if (n<=0)
        {
            printf ("send error\n");
            break;
        }
        memset(buff,0,128);
        n=recv(sockfd,buff,127,0);
        if(n<=0)
        {
            printf ("recv error\n");
            break;
        }
        printf ("%s\n",buff);
    }
    close(sockfd);
    exit(0);
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

直播预告 | 时序数据处理的云端利器:TDengine Cloud 详解与演示

当下&#xff0c;我们正处在一个万物互联的时代&#xff0c;大数据、云原生、AI、5G 等数字技术极大地方便了人们的生活&#xff0c;但智能物联网产生的海量数据却成为众多企业在数据处理上的巨大痛点。从本质来看&#xff0c;这些数据大多是产生自各种设备和传感器的时序数据&…

Spring种存取Bean的5种注解

存取Bean的五种注解 存储Bean对象两种方式1.添加一行bean2.使用注解的方式(5大注解)Controller(控制器存储)Service(服务存储)Repository(仓库存储)Component(组件存储)Configuration(配置存储)方法注解 Bean 获取Bean对象(三种)1.属性注入2.setter注入3.构造方法注入三种注入的…

springboot-分页功能

1.分页功能的作用 分页功能作为各类网站和系统不可或缺的部分&#xff08;例如百度搜索结果的分页等&#xff09; &#xff0c;当一个页面数据量大的时候分页作用就体现出来的&#xff0c;其作用有以下5个。 &#xff08;1&#xff09;减少系统资源的消耗 &#xff08;2&#…

Vue 3组件传值 、组件通信

本文采用<script setup />的写法&#xff0c;比options API更自由。那么我们就来说说以下七种组件通信方式&#xff1a; props emit v-model refs provide/inject eventBus vuex/pinia 举个例子 本文将使用下面的演示&#xff0c;如下图所示&#xff1a; 上图中…

mybatis粗心使用导致内存溢出

现象 服务响应变慢&#xff0c;线程日志也出现Java heap space内存溢出的错误&#xff0c;这个服务属于基础业务服务&#xff0c;出现问题要尽快的排查 分析 因为设置了gc日志和jmap启动相关参数 所以我们进行分析&#xff0c;这里模拟线上环境将堆大小参数调整到了128m&am…

【Linux】权限管理

文章目录 &#x1f4d6; 前言1. 什么是权限2. 权限管理2.1 Linux的用户分类&#xff1a;2.2 Liunx文件的分类&#xff1a;2.3 文件的访问权限2.4 文件访问权限的相关设置方法&#xff1a;chmod对文件权限的修改chown / chgrp 2.5 以八进制修改文件权限&#xff1a;2.6 默认权限…

Springsecurity课程笔记06-13章基于数据库的方法授权

动力节点Springsecurity视频课程 6 密码处理 6.1 为什么要加密&#xff1f; csdn 密码泄露事件 泄露事件经过&#xff1a;https://www.williamlong.info/archives/2933.html 泄露数据分析&#xff1a;https://blog.csdn.net/crazyhacking/article/details/10443849 6.2加密…

IJKPLAYER源码分析-常用API

前言 本文简要介绍IJKPLAYER的几个常用API&#xff0c;以API使用的角度&#xff0c;来审视其内部运作原理。这里以iOS端直播API调用切入。 调用流程 init 创建播放器实例后&#xff0c;会先调用init方法进行初始化&#xff1a; - (IJKFFMediaPlayer *)init {self [super ini…

计算机网络复习题+答案

文章目录 导文题目一、单项选择题二、填空题三、判断改错题,判断下列命题正误,正确的在其题干后的括号内打“√”,错误的打“”,并改正。四、名词解释五、简答题六、应用题导文 计算机网络复习题 题目 一、单项选择题 在应用层协议中,主要用于IP地址自动配置的协议是: (…

文案自动修改软件-文案自动改写的免费软件下载

文章生成器ai写作机器人 随着人工智能技术的飞速发展&#xff0c;越来越多的新型产品被推向市场。其中&#xff0c;文章生成器AI写作机器人是一个备受关注的新兴行业。它使用机器学习和自然语言处理等技术&#xff0c;为用户自动生成高质量的文章和内容&#xff0c;帮助用户在…

Python——第2章 数据类型、运算符与内置函数

目录 1 赋值语句 2 数据类型 2.1 常用内置数据类型 2.1.1 整数、实数、复数 2.1.2 列表、元组、字典、集合 2.1.3 字符串 2.2 运算符与表达式 2.2.1 算术运算符 2.2.2 关系运算符 2.2.3 成员测试运算符 2.2.4 集合运算符 2.2.5 逻辑运算符 2.3 常用内置…

本地搭建属于自己的ChatGPT:基于PyTorch+ChatGLM-6b+Streamlit+QDrant+DuckDuckGo

本地部署chatglm及缓解时效性问题的思路&#xff1a; 模型使用chatglm-6b 4bit&#xff0c;推理使用hugging face&#xff0c;前端应用使用streamlit或者gradio。 微调对显存要求较高&#xff0c;还没试验。可以结合LoRA进行微调。 缓解时效性问题&#xff1a;通过本地数据库…

Mybatis高级映射及延迟加载

准备数据库表&#xff1a;一个班级对应多个学生。班级表&#xff1a;t_clazz&#xff1b;学生表&#xff1a;t_student 创建pojo&#xff1a;Student、Clazz // Student public class Student {private Integer sid;private String sname;//...... }// Clazz public class Cla…

Flutter PC桌面端 控制应用尺寸是否允许放大缩小

一、需求 桌面端中&#xff0c;登录、注册、找回密码页面不允许用户手动放大缩小&#xff0c;主页面允许 二、插件 window_manager 使用教程请参照这篇博客&#xff1a;Flutter桌面端开发——window_manager插件的使用 题外话&#xff1a; 之前使用的是bitsdojo_window插件…

[golang gin框架] 25.Gin 商城项目-配置清除缓存以及前台列表页面数据渲染公共数据

配置清除缓存 当进入前台首页时,会缓存对应的商品相关数据,这时,如果后台修改了商品的相关数据,缓存中的对应数据并没有随之发生改变,这时就需要需改对应的缓存数据,这里有两种方法: 方法一 在管理后台操作直接清除缓存中的所有数据,当再次访问前台首页时,就会先从数据库中获取…

记frp内网穿透配置

这两天由于想给客户看一下我们的系统&#xff0c;于是想到用内网穿透&#xff0c;但是怎么办呢&#xff0c;没有用过呀&#xff0c;于是各处找资料&#xff0c;但是搞完以后已经不记得参考了那些文档了&#xff0c;对不起各位大神&#xff0c;就只能写出过程和要被自己蠢死的错…

初识C++(二)

在初识c&#xff08;一&#xff09;当中我们已经向大家介绍了四个c和C语言不同的使用方法。接下来我们再来向大家介绍另外的一些新的c语言的使用方法。 &#x1f335;引用 简单一点来说引用就是给已存在的变量起一个别名。这个别名通常的作用和C语言当中的指针类似。我们可以通…

牛客网刷题总结

1.利用%符号获取特定位数的数字。 2.强制类型转换 &#xff08;将float转换为int &#xff09; 3.计算有关浮点型数据时&#xff0c;要注意你计算过程中所有的数据都是浮点型 4.0/3.0 ! 4/3 4.通过位操作符实现输出2的倍数&#xff08;对于位操作符不熟悉的小伙伴可以看看我…

基于Java+SpringBoot+vue实现图书借阅和销售商城一体化系统

基于JavaSpringBootvue实现图书借阅和销售商城一体化系统 博主介绍&#xff1a;5年java开发经验&#xff0c;专注Java开发、定制、远程、指导等,csdn特邀作者、专注于Java技术领域 作者主页 超级帅帅吴 Java项目精品实战案例《500套》 欢迎点赞 收藏 ⭐留言 文末获取源码联系方…

电脑系统错误怎么办?您可以看看这5个方法!

案例&#xff1a;电脑出现系统错误该如何解决&#xff1f; 【这几天长时间使用我的电脑&#xff0c;导致它的系统出现了错误。有没有小伙伴知道如何解决电脑系统出错的问题&#xff1f;求一个能快速解决的方法。】 电脑系统出现错误是使用电脑时难免会遇到的问题之一&#xf…