IO多路复用之select

管道

管道文件:进程间通信机制在文件系统的映射(linux下一切皆文件)。 管道分为有名管道(named pipe),和匿名管道,named表示在文件系统的路径。

使用linux命令创建管道文件:

$ mkfifo pipename.pipe			#虽然linux不已后缀名区分文件类型,但通常用后缀来表示他是一个管道文件

管道按通信方式分:

  • 单工通信:数据只能从A端到B端
  • 半双工通信:A->B,也可B->A,但是不能同时发数据。
  • 全双通信:A->B的同时,也可以B->A.

可以使用两根单工通信管道实现A<->B的即时通信。

打开管道文件

1A负责发消息,B负责接收消息

//A.c 写端
#include <sys/types.h>
#include <sys/stat.h>
#include <fcnt.h>

int main(int argc, int argv[]){
    
    ARGS_CHECK(argc,2);
    int fdw = open(argv[1],O_WRONLY);
    ERROR_CHECK(fd, -1 , "open fdw");
    
    write(fdw,"hello world!");
    
    close(fdw);
    return 0;
}

//B.c
//头文件略。

int main(int argc, char argv[]){
    ARGS_CHECK(argc,2);
    
    int fdr = open(argv[1],O_RDONLY);
    ERROR_CHECK(fdr,-1,"open fdr");
    
    char buf[1024];
    read(fdr,buf,sizeof(buf));
    printf("%s\n",buf);
    
    close(fdr);
    return 0;
}

🛰==注意:==对于以O_RDONLY/O_WRONLY打开的管道文件,若只打开一端会导致进程阻塞,直到

另一端也打开才会就绪。

读写管道的注意事项:
  • 若读缓冲区没有数据,read函数会引发进程阻塞,知道进程有数据来往。

  • 若写缓冲区有数据,write函数会导致写阻塞,直到接受端将数据取走。

管道的关闭注意事项:
  • 写端先关闭,读端再调用read,会导致写一直就绪,但read()函数返回的是0;

  • 读端先关闭,然后在调用write,会导致程序异常终止。

串行等待:阻塞式通信

服务器在处理多个客户端请求时,当被处理的客户端由于需要资源被动阻塞了,那么他同时会阻止服务器处理其他的请求,就算其他请求处于就绪状态也不行。

并行等待:非阻塞式通信

特点是:不会因为某一客户端因读写操作阻塞而影响其他客户端的通信。

IO多路复用机制之select:

模型大致分为四个步骤:

  • 设置监听
  • 用户等待(轮询算法)
  • 直到任一监听的资源就绪,唤醒用户
  • 找到就绪的资源,执行对应IO操作

selece函数是一种基于IO复用机制,它可以同时监听多个文件描述符的状态,一旦其中有任意一个文件描述符就绪(读写时间发生),就会返回给调用者,这种机制让多个进程处理多个网络链接的读写操作,提高并发性能。

select的函数声明如下:

int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, struct timeval* timeout);

//nfds:所有监听集合中fd的值最大的,再加1;
//readfds:读时间集合
//writefds:写时间集合
//exceptfds:异常时间集合
//timeout:超时时限


使用select函数实现A、B用户即时聊天程序:

yfg@yfg-Machine:~/cpp_50/linux10/study$ ls
1.pipe  2.pipe  aqiang  aqiang.c  azhen  azhen.c  Makefile
#准备两根管道,实现即时通信。
//aqiang.c
#include <50func.h>
int main(int argc, char *argv[])
{
    // ./4_aqiang_select 1.pipe 2.pipe
    ARGS_CHECK(argc,3);
    int fdr = open(argv[1],O_RDONLY);
    ERROR_CHECK(fdr,-1,"open fdr");
    int fdw = open(argv[2],O_WRONLY);
    ERROR_CHECK(fdw,-1,"open fdw");
    printf("aqiang is OK\n");
    char buf[4096];
    fd_set rdset;//读集合
    while(1){
        FD_ZERO(&rdset); //初始化监听集合
        FD_SET(STDIN_FILENO,&rdset);//将标准输入加入监听
        FD_SET(fdr,&rdset);//将管道的读端加入监听
        select(fdr+1,&rdset,NULL,NULL,NULL);
        //最大数值的fd 是 fdr
        //读监听集合 rdset
        //写监听集合 异常集合没有
        //永久等待
        //
        //从select返回以后,rdset是就绪集合
        //遍历就绪集合
        ssize_t sret;
        if(FD_ISSET(STDIN_FILENO,&rdset)){//从标准输入中获取数据
            memset(buf,0,sizeof(buf));
            sret = read(STDIN_FILENO,buf,sizeof(buf));
            if(sret == 0){ //主动退出
                write(fdw,"nishigehaoren",13);
                break;
            }
            time_t now = time(NULL);
            char *p = ctime(&now);
            write(fdw,p,strlen(p));
            write(fdw,buf,strlen(buf));
        }
        if(FD_ISSET(fdr,&rdset)){
            memset(buf,0,sizeof(buf));
            sret = read(fdr,buf,sizeof(buf));
            if(sret == 0){//对方已经终止了
                printf("Hehe\n");
                break;
            }
            printf("buf = %s\n", buf);
        }
    }
    close(fdr);
    close(fdw);
    return 0;
}

//azhen.c
#include <50func.h>
int main(int argc, char *argv[])
{
    // ./4_azhen_select 1.pipe 2.pipe
    ARGS_CHECK(argc,3);
    int fdw = open(argv[1],O_WRONLY);
    ERROR_CHECK(fdw,-1,"open fdw");
    int fdr = open(argv[2],O_RDONLY);
    ERROR_CHECK(fdr,-1,"open fdr");
    printf("azhen is OK\n");
    char buf[4096];
    fd_set rdset;//读集合
    time_t lastactive = time(NULL);
    while(1){
        FD_ZERO(&rdset); //初始化监听集合
        FD_SET(STDIN_FILENO,&rdset);//将标准输入加入监听
        FD_SET(fdr,&rdset);//将管道的读端加入监听
        struct timeval timeout;
        timeout.tv_sec = 1;
        timeout.tv_usec = 0;
        select(fdr+1,&rdset,NULL,NULL,&timeout);
        time_t curtime = time(NULL);
        printf("curtime = %s\n", ctime(&curtime));
        ssize_t sret;
        if(FD_ISSET(STDIN_FILENO,&rdset)){//从标准输入中获取数据
            memset(buf,0,sizeof(buf));
            sret = read(STDIN_FILENO,buf,sizeof(buf));
            if(sret == 0){ //主动退出
                write(fdw,"nishigehaoren",13);
                break;
            }
            time_t now = time(NULL);
            char *p = ctime(&now);
            write(fdw,p,strlen(p));
            write(fdw,buf,strlen(buf));
        }
        if(FD_ISSET(fdr,&rdset)){
            lastactive = time(NULL); //更新阿强的活跃时间
            memset(buf,0,sizeof(buf));
            sret = read(fdr,buf,sizeof(buf));
            if(sret == 0){//对方已经终止了
                printf("Hehe\n");
                break;
            }
            printf("buf = %s\n", buf);
        }
        if(curtime - lastactive > 10){
            write(fdw,"byebye",6);
            break;
        }
    }
    close(fdr);
    close(fdw);
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值