linux io多路复用基本原理和实现

一 io多路复用模型

在这里插入图片描述

二 io多路复用下的阻塞与不阻塞io模式

阻塞io模式

阻塞模式 :需要等待一个功能完成了  才能到下一个实现 

在这里插入图片描述

比如 write read 举例 一句话 概括  读写不可以同时进行 也就是顺序执行  
就很好体现出阻塞模式的说法了 要么在读的时候 写在等待  相反也是 

在这里插入图片描述

但是如果写一直不结束 或者 读出来的数据 是空 的 
这样的就会永远发生阻塞了 就会哞哞不动了 

不阻塞io模式
在这里插入图片描述
非阻塞io模式:

非阻塞:不需要询问是否同意,不需要等待可以直接运行下一个工作。
第一点和第二点:
如果让一个设置为非阻塞通信模式的函数 等待 那就会出错 让它返回一个错误信息。
非阻塞目标地址的数据会被测试  是否是可读的 也就是 存不存在数据 
第三 :如果不停对 cpu 发起读写  很消耗他的资源 。

三 形成了对比 突出了他的优点

在这里插入图片描述

io多路复用 顾名思义 就是 用一个io 重重复复 用了 几次  
也就是 实际存在的东西只有一个 但是 映射出来了好几个 内存只会占一个 
基本思路 

在这里插入图片描述
和函数存入地址和返回点执行函数 是一样的

1 他会把函数地址Z暂时存入R14 寄存器 到了函数返回 再从R14取出 地址 
最后到了函数点 执行 (不懂的 看我博客的《上电到main.C的过程》)

四 select模型

在这里插入图片描述
前后发生的改变

(前)先有地址 (后 )才可以存数据  存了就可以读 
实现的框架 

在这里插入图片描述

五 代码实现

实现目标作业

1 实现 服务器 和客户端 全双工模式 
写的同时 可以读到数据  并且对方可以接收到数据 
服务器只能作为 读取 或者发送中转站

在这里插入图片描述

大概的框架 是这样的 
服务器的大圈 中 的小圈 表示  write 只能为一个中转站 
也就是 客户端 用间接方式 读了他写的数据

步骤 (函数名都是大写(除select外) 有点写错了 )
在这里插入图片描述
在这里插入图片描述

1客户端是 间接读取它的数据 和本来就是具有写的操作
2 最后一个参数是时间的参数 用于规定时间 超了就是异常 
3除了圈之外 全部设置为NULL
在多线程的基础上改 

客户端

#include<stdio.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<string.h>
#include<stdlib.h>

int  main(int argc,char **argv)
{
      int fd =-1;
      struct sockaddr_in  sin;
      if(argc!=3)
      {
       exit(1);
      }
      int port =atoi(argv[2]);
      //XXXX  192.186.2.129 5003
      fd=socket(AF_INET,sock_stream,0);
     
      if(fd<0)
      {
         perror("socket");
        exit(1);
      }
      
      bzero(&sin,sizeof(sin));
      
      sin.sin_family=AF_INET;
      sin.sin_port=htons(port);
      sin.sin_addr.s_addr=inet_addr(argv[1]);
      //XXXX  192.186.2.129 5003 
     if(connect(fd,(struct sockaddr*)&sin,sizeof(sin))<0)
     {
           perror("connect");
            exit(1);
     }
     
       fd_set rest=-1; 
       int  maxfd=-1;
       struct timeval tout;
       char buf[1024];
       int rts=-1;
     while(1)
    {
         
         FD_ZERO(&rest);
         FD_SET(0,&rest); //0开始;  //0----stdin (标准io模式) 
         FD_SET(fd,&rest);//最大可到1024;
         tout.tv_sec=5; //5秒
         tout.tv_usec=5; //0微妙 0-5的区间判断 
                                    
          maxfd=fd;//下标  所以要加一  
         
         select(maxfd+1,&rest,NULL,NULL,&tout);//观察前后数据变化 
         
          //0----stdin (标准io) 
        if(FD_ISSET(0,&rest)) //对应集合 FD_SET(0,&rest);//最大可到1024;
        {
            bzero(buf,1024);
            do
            { 
                rts=read(0,buf,1023);
            }while(rts<0);
            
            if(rts<0)
            {
                continue;
            }
            if(!rts)break;
            
            if(write(fd,buf,strlen(buf))<0)
            {
                break;
            }
            
            if(strncasecmp(buf,"QUIT",strlen("QUIT")))//输入退出 
            {
                break;
            }
            
        }
        
          if(FD_ISSET(fd,&rest)) //对应集合 FD_SET(fd,&rest);//最大可到1024;
        {
            bzero(buf,1024);
            do
            { 
                rts=read(fd,buf,1023);
            }while(rts<0);
            
            if(rts<0)
            {
                continue;
            }
            if(!rts)break;
            
            if(write(fd,buf,strlen(buf))<0)
            {
                break;
            }
            printf("seivel saiy is %s\n",buf);
            
            if(strncasecmp(buf,"QUIT",strlen("QUIT")))//输入退出 
            {
                break;
            }
            
        }
             
    }


     return 0;
}

服务器

#include<stdio.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<string.h>
#include<stdlib.h>
#include<pthread.h>

void child(void *arg);//声明
void kill_wait(int num)
{
   if(num==SIGCHLD)
   {
   waitpid(-1,NULL,WNOHANG);
   }
}
int  main()
{
      int fd =-1;
      struct sockaddr_in  sin;
      
      fd=socket(AF_INET,sock_stream,0);
      kill_wait(SIGCHID);
      if(fd<0)
      {
         perror("socket");
        exit(1);
      }
      
      bzero(&sin,sizeof(sin));
      
      sin.sin_family=AF_INET;
      sin.sin_port=htons(5002);
    //  sin.sin_addr.s_addr=inet_addr("192.168.2129");
       sin.sin_addr.s_addr=INADDR_INY;
      if(bind(fd,(struct sockaddr *)&sin,sizeof(sin))<0)
     {
              perror("bind");
               exit(1);
     }
     
     if(listen(fd,5)<0)
     {
            perror("listen");
               exit(1);
     }
     
     int newfd=-1;
     char buf[1024];
     struct sockaddr_in cin;
     socklen len=sizeof(cin);
      pid_t pid;
   while(1)
   {
     newfd=accept(fd,(struct sockaddr* )&cin,&len);//&ci哪里都存有端口号和 i 地址了但是现在为网络字节序 需要转为主机字节序
    
     if(newfd<0)
     {
            perror("accept");
               exit(1);
     }
    char ipv4_buf[16];//比如 192.168.2.129+'/' ==14个字符   所以数组不要过多大这个大小
  if(!inet_ntop(AF_INET,(void *)&cin.sin_addr.s_addr,ipv4_buf,sizeof(cin)))
  {
          perror("inet_ntop");
               exit(1);
  }
   printf("IPV4=====%s port====%d\n",ipv4_buf,ntohs(cin.sin_port));
                      //ntohs函数网络字节序转为主机字节序
     pid=fork();

   if(pid<0)
   {
      break;
   }
   if(pid==0)
   {
     close(fd);
     child(&newfd);// 子进程工作 
  }
  if(pid>0)
   {
     close(newfd);
     
  }
}                              
   
     
         close(fd);
        close(newfd);
        return 0}


void child(void *arg)
{
        char buf[1024];
       int rts=-1;
       int newfd=*(int *)arg;
       int ws=-1;
        while(1)
     {
        
         do
            {
           bzero(buf,1024);
           rts=read(newfd,buf,1023);
            
         }while(rts<1);//while(rts<1);==rts>1
    
         if(rts<0)
         {
             exit(1);
         }
           if(rts==0)
         {
              break;
        }
         printf("read is %s\n",buf);
      
         do  //中转站 发送回去 
         {
            ws=write(newfd,buf,strlen(buf));
          }while(ws<0);
         
         if(strncasecmp(buf,"QUIT",strlen("QUIT")))//输入退出 
            {
                break;
            }
     }
          close(fd);
        close(newfd);
}     

在这里插入图片描述

客户端输入"QUIT" 退出 到命令行 

在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值