• IO的多路复用和信号驱动


      Linux为多路复用IO提供了较多的接口,有select(),pselect(),poll()的方式,继承自BSD和System V 两大派系。

      select模型比较简单,“轮询”检测fd_set的状态,然后再采取相应的措施。

      信号驱动模型有必要仔细研究一下,一般有如下步骤:

    1. 设置安装函数,信号是驱动信号是SIGIO(最好使用sigaction的方式,方便设置flag为SA_RESTART,因为client中读取终端的syscall可能会被中断,有必要重启。当然,使用signal()的方式然后再对errno进行判断是否为ETNTR,自行重启也是一种方法。但是signal()的可移植性问题,我强烈不建议使用)
    2. 设置fd的属主。F_SETOWN,要接受信号的进程,fcntl().
    3. 设置fd的异步标志。小细节,用'|'添加,要把之前的状态保留,也就是先F_GETFL再F_SETFL。(注意:在打开文件open()时设置标识O_ASYNC无实质效果)  

      On Linux, specifying the O_ASYNC
      flag when calling open() has no effect. To enable signal-driven I/O, we must
      instead set this flag using the fcntl() F_SETFL operation (Section 5.3).————《the linux programming interface》 4.3.1

    4. sigaction()安装

      具体进下文client例程。

      写了一个聊天程序的demo,把这两种技术都使用了。服务端采取多路复用的IO方式,代替多进(线)程的模型,客服端采取的是信号驱动,如下:

    容易产生bug的地方都写注释里边了。

    serv.c

      1 #include <sys/select.h>
      2 #include <sys/socket.h>
      3 #include <netinet/in.h>
      4 #include <arpa/inet.h>
      5 #include <unistd.h>
      6 #include <fcntl.h>
      7 #include <stdlib.h>
      8 #include <string.h>
      9 #include <stdio.h>
     10 #include <signal.h>
     11 
     12 void endServ(int sig)
     13 {
     14     printf("Server ended!
    ");
     15     exit(-1);
     16 }
     17 
     18 int main()
     19 {
     20     // signal
     21     struct sigaction act;
     22     sigemptyset(&act.sa_mask);
     23     act.sa_handler = endServ;
     24     act.sa_flags = 0;
     25     sigaction(SIGINT,&act,0);
     26     printf("This server is started,enter CTRL+C to end. 
    ");
     27 
     28     int servfd = socket(AF_INET,SOCK_STREAM,0);
     29     if(servfd == -1) {
     30         printf("something wrong with socket():%m
    ");
     31         exit(-1);
     32     }
     33 
     34     struct sockaddr_in addr;
     35     addr.sin_family = AF_INET;
     36     addr.sin_port = htons(9999);
     37     inet_pton(AF_INET,"127.0.0.1",&addr.sin_addr);
     38 
     39     if (bind(servfd,(struct sockaddr*)&addr,sizeof(addr)) == -1) {
     40         printf("Some thing wrong with bind():%m
    ");
     41         exit(-1);
     42     }
     43     printf("Bind success!
    ");
     44 
     45     listen(servfd,20);
     46     
     47     int numbers=0;//how many clients has accepted
     48     fd_set fs;
     49     FD_ZERO(&fs);
     50     int client_fd[100];
     51     int i;
     52     for(i=0;i<100;++i)
     53     {
     54         client_fd[i] = -1;
     55     }
     56 
     57     int maxfd=0;
     58     char buf[1024];
     59     for(;;)
     60     {
     61         maxfd =0;
     62         FD_ZERO(&fs);
     63         FD_SET(servfd,&fs);
     64         maxfd = maxfd>servfd?maxfd:servfd;
     65         for(i=0;i<numbers;++i)
     66         {
     67             if(client_fd[i] != -1) {
     68                 FD_SET(client_fd[i],&fs);
     69                 maxfd = maxfd>client_fd[i]?maxfd:client_fd[i];
     70             }
     71         }
     72 
     73         int res = select(maxfd+1,&fs,0,0,0);
     74         if(res == -1) {
     75             printf("Something wrong with select():%m
    ");
     76             exit(-1);
     77         }
     78 
     79         if(FD_ISSET(servfd,&fs) && numbers < 100) {
     80             printf("New client!
    ");
     81             client_fd[numbers] = accept(servfd,0,0);
     82             numbers++;
     83         }
     84         
     85         for(i=0;i<numbers;++i)
     86         {
     87             bzero(buf,sizeof(buf));
     88             //judge if client_fd[i] equal -1 is necessary
     89             //if a client quited,next time the program will
     90             //have a segment default
     91             //also it should be in the front.
     92             if(client_fd[i] != -1 && FD_ISSET(client_fd[i],&fs)) 
     93             {
     94                 res = recv(client_fd[i],buf,sizeof(buf),0);
     95                 if(res == 0) {
     96                     printf("A client quit
    ");
     97                     close(client_fd[i]);
     98                     FD_CLR(client_fd[i],&fs);
     99                     client_fd[i] = -1;
    100                 }
    101                 else if(res == -1) {
    102                     printf("Something wrong with net.
    ");
    103                     exit(-1);
    104                 }
    105                 else {
    106                     int j;
    107                     for(j=0;j<numbers;++j)
    108                     {
    109                         if(client_fd[j] != -1)
    110                             send(client_fd[j],buf,sizeof(buf),0);
    111                     }
    112                 }
    113             }
    114         }
    115     }
    116 }

    client:

     1 #include <signal.h>
     2 #include <unistd.h>
     3 #include <sys/socket.h>
     4 #include <fcntl.h>
     5 #include <arpa/inet.h>
     6 #include <netinet/in.h>
     7 #include <stdio.h>
     8 #include <string.h>
     9 #include <stdlib.h>
    10 #include <errno.h>
    11 
    12 static int fd;
    13 
    14 void show(int sig)
    15 {
    16     char buf[1024] = {0};
    17     int n = read(fd,buf,sizeof(buf));
    18     buf[n] = 0;
    19     write(1,"MSG:",strlen("MSG:"));
    20     write(1,buf,strlen(buf));
    21 }
    22 
    23 int main()
    24 {
    25     struct sigaction act;
    26     sigemptyset(&act.sa_mask);
    27     act.sa_handler = show;
    28     //This is necessary,in last loop read() counld be interrupt;
    29     act.sa_flags = SA_RESTART;
    30     sigaction(SIGIO,&act,0);
    31 
    32     fd = socket(AF_INET,SOCK_STREAM,0);
    33     if(fd == -1) {
    34         printf("Cannot get socketfd!:%m
    ");
    35         exit(-1);
    36     }
    37 
    38     struct sockaddr_in addr;
    39     addr.sin_family = AF_INET;
    40     addr.sin_port = htons(9999);
    41     inet_pton(AF_INET,"127.0.0.1",&addr.sin_addr);
    42 
    43     if(connect(fd,(struct sockaddr*)&addr,sizeof(addr)) != -1)
    44         printf("connect success!
    ");
    45     else
    46         exit(-1);
    47 
    48     int flag = fcntl(fd,F_GETFL);
    49     flag |= O_ASYNC;
    50     fcntl(fd,F_SETFL,flag);
    51     fcntl(fd,F_SETOWN,getpid());
    52 
    53     char buffer[1024]={0};
    54     for(;;)
    55     {
    56         int n = read(0,buffer,sizeof(buffer));
    57         if(n==-1)
    58             break;
    59         send(fd,buffer,n,0);
    60     }
    61 
    62     write(1,"Closed.",strlen("Closed."));
    63 }
  • 相关阅读:
    list与map的区别
    [BJOI2019]光线——递推
    [BJOI2019]奥术神杖——AC自动机+DP+分数规划+二分答案
    [BJOI2019]送别——非旋转treap
    [BJOI2019]排兵布阵——分组背包
    [LOJ3014][JOI 2019 Final]独特的城市——树的直径+长链剖分
    BZOJ2300[HAOI2011]防线修建——非旋转treap+凸包(平衡树动态维护凸包)
    BZOJ3033太鼓达人——哈密顿回路/欧拉回路
    BZOJ3236[Ahoi2013]作业——莫队+树状数组/莫队+分块
    BZOJ2339[HNOI2011]卡农——递推+组合数
  • 原文地址:https://www.cnblogs.com/ittinybird/p/4574397.html
Copyright © 2020-2023  润新知