首页 > 其他分享 >IO多路复用

IO多路复用

时间:2024-08-16 19:05:42浏览次数:10  
标签:addr 多路复用 描述符 fd IO 接字 include

概述

提高通信的效率(对单个进程来说),在客观的环境下发送和接收是不可能同时接近并发的,
可以实现单进程(像同时)发送和接收
针对发送的文件描述符是:套接字write,标准输入:stdin  /dev/stdin   往 /dev/stdin读数据
针对接收的文件描述符是:套接字 ,  标准输出 printf("hello");   把hello往 /dev/stdout文件写

① 多路复用的基本概念
最大的限度提高当进程的工作效率(模拟并发,但是因为是模拟,肯定不是完全意义上的并发,轮询机制监听多个文件描述符,实现多客户访问)
最基本的工作:
    发信息
    收信息
    
多路复用的接口:
    select
    poll
    epoll

② select多路复用的基本理解

    在没有多路复用:
            如果我们想同时接受和发送消息,一般会创建俩条线程,一条去读,一条去写(用两条线程去监控两个文件描述符:   标准输入0  和 套接字文件描述符)

    
    有多路复用:监听需要监听的文件描述符,轮询(循环)机制来监听文件描述符
        文件描述符有三个模式:可不可读 ,可不可写,可不可执行
        read(文件描述符,); 3
        scanf();监视标准输入的文件描述符
        scanf: stdin  监控可不可读  0
        read: 套接字  监控可不可读
        while(1)
        {
                 select() 0 4  文件描述符的取值范围
                {
                   4
                    while(1)
                    {
                        for(int n=0; n<要监控的文件描述符+1; n++) 
                        {
                                里面的代码就是判断循环到的n是否可读、是否可写、是否可执行、
                                if(如果这个文件描述符可读了) break;
                        }    
                        break;    
                    }
                    
                    return 告诉我现在找到了可读的文件描述符
                }
                
                if(是不是可以发送啦?)
                {
                    scanf()
                    write
                }
                
                if(是不是可以读取)
                {
                    我在外面知道了这个文件描述符可以读取,我再去调用 read 这个文件描述符
                   read  
                }
        }
 
譬如该多路复用用于服务器,那么需要监听那两个文件描述符:_客户端套接字(读)12__ 和 __标准输入(发送)_0_


客户端TCP多路复用使用代码思路
        创建套接字
        连接服务器
        
        -----多路复用----
        创建文件描述符集
        清空文件描述符集
        添加要监听的文件描述符到 文件描述符集里面
        
        while(1)
        {
            调用select进行监听集合中的文件描述符
            select();
            
            if(能不能去读键盘啊)
            {
                scanf()
                write()    
            }
            
            if(能不能去接收信息啊)
            {
                read();
                printf("");    
            }
        }
        
        关闭套接字
        
服务器TCP多路复用使用代码思路
        创建套接字
        绑定套接字
        监听
        等待连接
        
        创建文件描述符集
        清空文件描述符集
        把要监听的标准输入+套接字(客户端的对等套接字)添加到文件描述符集中
        while(1)
        {
            调用select进行监听集合中的文件描述符
            select();
            
            if(能不能去读键盘啊)
            {
                scanf()
                write()    
            }
            
            if(能不能去接收信息啊)
            {
                read();
                printf("");    
            }
        }
        
        关闭套接字

select函数描述

image


③ select多路复用服务器

/******************************************************************************************************
 * @file name:		  :  select多路复用服务器.c
 * @brief  		      : 多路复用
 * @author 		      : [email protected]
 * @date 			  : 2024/08/16
 * @version      	  : V1.0
 * @property 		  : 暂无
 * @note   		      : None
 * CopyRight (c)  2023-2024   [email protected]   All Right Reseverd
 ******************************************************************************************************/

/*********************************************头文件***************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
/**********************************************END*****************************************************/



int main()
{
    //创建服务器套接字
    int s_fd = socket(AF_INET,SOCK_STREAM,0);
    if(s_fd == -1)
    {
        perror("socket");
        return -1;
    }

    //定义结构体变量
    struct sockaddr_in ser_addr;
    memset(&ser_addr,0,sizeof(ser_addr));

    ser_addr.sin_family      = AF_INET;
    ser_addr.sin_port        = htons(8888);
    ser_addr.sin_addr.s_addr = htonl(INADDR_ANY);

    //绑定
    if(bind(s_fd,(struct sockaddr *)&ser_addr,sizeof(ser_addr)) == -1)
    {
        perror("bind");
        return -1;
    }

    if(listen(s_fd,5) == -1)
    {
        perror("listen");
        return -1;
    }


    int c_fd = accept(s_fd,NULL,NULL);
    if(c_fd == -1)
    {
        perror("accept");
        return -1;
    }


    //使用多路复用 进行监听而不是创建两条线程去双向通信

    //创建一个文件描述符的集合,把要监视的文件描述符添加进这个集合里面
    fd_set fs;

    char msg[20];
    while(1)
    {
        //清空这个集合
        FD_ZERO(&fs);
        //再把两个文件描述符添加进去
        FD_SET(c_fd,&fs);
        FD_SET(STDIN_FILENO,&fs);
        //再调用select去监视 ---- 默认是堵塞的
        memset(msg,0,20);

        if(-1 == select(c_fd+1,&fs,NULL,NULL,NULL))
        {
            perror("select");
            return -1;
        }

        if(FD_ISSET(c_fd,&fs))//判断是不是c_fd可以读了
        {
            read(c_fd,msg,20);
            printf("%s\n",msg);
        }


        if(FD_ISSET(STDIN_FILENO,&fs))////判断是不是STDIN_FILENO可以读了
        {
            printf("请输入要发送的消息");
            scanf("%s",msg);
            write(c_fd,msg,strlen(msg));
        }
    }


    close(s_fd);

    return 0;
}

/******************************************************************************************************
 * @file name:		  : tcp多路复用客户端.c
 * @brief  		      : 客户端的多路复用
 * @author 		      :[email protected]
 * @date 			      :2024/08/16
 * @version      	  :V1.0
 * @property 		    :暂无
 * @note   		      :None
 * CopyRight (c)  2023-2024   [email protected]   All Right Reseverd
 ******************************************************************************************************/

/*****************************************头文件********************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
/******************************************END*********************************************************/

int main()
{
    
    int c_fd = socket(AF_INET,SOCK_STREAM,0);
    if(c_fd == -1)
    {
        perror("socket");
        return -1;
    }

    struct sockaddr_in ser_addr;
    memset(&ser_addr,0,sizeof(ser_addr));

    ser_addr.sin_family      = AF_INET;
    ser_addr.sin_port        = htons(8888);
    ser_addr.sin_addr.s_addr = inet_addr("192.168.12.99");
    

    if(-1 == connect(c_fd,(struct sockaddr *)&ser_addr,sizeof(ser_addr)))
    {
        perror("connect");
        return -1;
    }


    //使用多路复用 进行监听而不是创建两条线程去双向通信

    //创建一个文件描述符的集合,把要监视的文件描述符添加进这个集合里面
    fd_set fs;

    char msg[20];
    while(1)
    {
        //清空这个集合
        FD_ZERO(&fs);
        //再把两个文件描述符添加进去
        FD_SET(c_fd,&fs);
        FD_SET(STDIN_FILENO,&fs);
        //再调用select去监视 ---- 默认是堵塞的
        memset(msg,0,20);
        
        if(-1 == select(c_fd+1,&fs,NULL,NULL,NULL))
        {
            perror("select");
            return -1;
        }

        if(FD_ISSET(c_fd,&fs))//判断是不是c_fd可以读了
        {
            read(c_fd,msg,20);
            printf("%s\n",msg);
        }


        if(FD_ISSET(STDIN_FILENO,&fs))////判断是不是STDIN_FILENO可以读了
        {
            printf("请输入要发送的消息");
            scanf("%s",msg);
            write(c_fd,msg,strlen(msg));
        }
    }

    close(c_fd);
    return 0;
}

⑤ 设置套接字为非堵塞状态

多路复用的时候,为什么要设置套接字为非堵塞状态?
在使用 poll()和select() 进行多路复用时,设置套接字为非阻塞是一个常见的做法,但并不是必须的。设置套接字为非阻塞的目的是为了确保 poll()和select(??,) 在等待事件时不会被阻塞,从而能够处理其他事件或执行其他任务。

如果套接字被设置为阻塞模式,并且没有数据可读或者写入操作无法立即完成,poll() 将会被阻塞,直到有事件发生或者超时。在这种情况下,poll()和select() 将无法实现真正的多路复用,因为它会一直等待某个套接字上的事件,而不会检查其他套接字。

因此,尽管不是绝对必须的,但为了实现有效的多路复用,通常建议将套接字设置为非阻塞模式。这样一来,poll()和select() 将能够同时监视多个套接字,当任何一个套接字上发生事件时都能及时响应,而不会被阻塞。

使用fcntl设置套接字为非阻塞:
    fcntl的作用:
    复制文件描述符(拓展非主要)、获取/设置文件描述符标志、获取/设置记录锁(拓展非主要)、获取/设置异步 I/O 执行状态(拓展非主要)、获取/设置套接字选项(拓展非主要)
    
    示例:
    基本流程思路:获取套接字标志,设置标志为非堵塞对,再把具有非堵塞的标志设置到套接字中
    
    // 创建套接字
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    // 设置非阻塞模式
    flags = fcntl(sockfd, F_GETFL, 0);
    if (flags == -1) {
        perror("fcntl");
        close(sockfd);
        exit(EXIT_FAILURE);
    }
    flags |= O_NONBLOCK;
    if (fcntl(sockfd, F_SETFL, flags) == -1) {
        perror("fcntl");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

标签:addr,多路复用,描述符,fd,IO,接字,include
From: https://www.cnblogs.com/hhail08/p/18363465

相关文章

  • Axios请求使用params参数导致后端获取数据嵌套
    问题重述:首先看前端的axios请求这里我使用params参数将data数据传给后端letdata=JSON.stringify(this.posts);axios.post("/blog_war_exploded/insertPost",{params:{data:data......
  • session概念和底层原理——生命周期
    session–(会话)一、概念session在网络应用中称为“会话控制”,是服务器为了保存用户状态而创建的一个特殊的对象。简而言之,session就是一个对象,用于存储信息。二、使用和注意事项session是以键值对的形式存放数据,类型为**<String,Object>**,通过getAttribute()和setAttri......
  • 文心快码Baidu Comate 帮你解大厂面试题:Java G1 GC中,region是什么意思?有哪些不同的reg
    ......
  • ChatGPT Is a Knowledgeable but Inexperienced Solver: An Investigation of Commons
    文章目录题目摘要简介什么是常识GPT能否有效回答常识问题?GPT是否知道回答问题的常识性知识?GPT是否具备常识性知识?GPT能否有效利用语境中的常识进行推理?相关工作结论与讨论题目ChatGPT是一个知识渊博但缺乏经验的解决者:对大型语言模型中常识问题的调查论文地......
  • 5.vue中axios封装工程化
    vue工程化中axios封装视频演示地址:https://www.bilibili.com/video/BV121egeQEHg/?vd_source=0f4eae2845bd3b24b877e4586ffda69a通常我们封装需要封装request.js基础的发送请求工具类,再根据业务封装service类,service类是具体业务的接口封装,在页面上直接调用的是servive类......
  • [Paper Reading] Single-to-Dual-View Adaptation for Egocentric 3D Hand Pose Estim
    名称Single-to-Dual-ViewAdaptationforEgocentric3DHandPoseEstimation时间:CVPR2024机构:TheUniversityofTokyoTL;DR多目3Dhandposeestimation数据比较难标注,作者核心思路是先训练单目模型,利用无监督的方法适配到双目场景,好处是a.无需标多目数据;b.可以适应任何......
  • 《荒野大镖客2》游戏闪退提示“缺失version.dll的文件”该怎么修复系统?荒野大镖客2游
    x3daudio1_7.dll是DirectX中的一个重要音频处理模块。它主要负责处理与3D音频相关的复杂功能,提供了一系列关键的音频处理能力,包括实现多个音频流的混合,将不同音频数据按规则叠加和处理以生成最终音频输出;对音频数据进行滤波,去除噪声等干扰信号以提高音频质量;提供如均衡器、混响......
  • 【A GUIDE TO CRC ERROR DETECTION ALGORITHM】 (译文3-Todo)
    11."Reflected"Table-DrivenImplementations“反射”表驱动实现Despitethefactthattheabovecodeisprobablyoptimizedaboutasmuchasitcouldbe,thisdidnotstopsomeenterprisingindividualsfrommakingthingsevenmorecomplicated.Toundersta......
  • IOC容器和依赖倒置
    1依赖倒置依赖倒置的核心价值:如果没有依赖倒置,全部都是依赖细节,如果分层架构是A层---B层--C层---D层---E层---F层,下层的修改,可能会导致上层随之改变,F层如果改变,E层要改,D层要改,C层要改......影响很大,成水波式向上影响,架构就的极度不稳定。如果都是依赖于抽象的,抽象即接口或抽......
  • Clion控制台中文输出/报错信息乱码的最完美解决方案(无需更改注册表,beta版UTF-8)
    Clion控制台中文输出/报错信息乱码的最完美解决方案(无需更改注册表,beta版UTF-8)1.问题:clion控制台乱码2.错误解决方案:Ctrl+Shift+Alt+/,回车,打开注册表,取消勾选"run.processes.with.pty"(clion可能会卡死)3.正确方式:1.Ctrl+Alt+S打开设置2.找到编辑器Editor-文件编码Fil......