【问题标题】:How to chat with client from a server using FIFO with select system call?如何使用带有选择系统调用的 FIFO 从服务器与客户端聊天?
【发布时间】:2021-02-16 15:56:00
【问题描述】:

您好,我正在尝试制作这样一个程序,其中两个 fds stdinfifoselect() 监控并相互通信。 select() 将监视 fifo 准备阅读或 stdin

服务器.c

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>
#include <sys/select.h>
int main(int argc,char *argv[]){
    int f,fifo_read,fifo_write,status;
    fd_set readset;
    FD_ZERO(&readset);
    char str[512]="start";
    if(argc !=2)
    if(argc != 2){
    printf("\nError: %s required argument [Fifo Name]\n\n",argv[0]);
            exit(EXIT_FAILURE);
    }

    if ((open(argv[1], O_RDWR)) < 0){
        f = mkfifo(argv[1],S_IRWXU);
        if(f<0){
            perror("Error While Creating FIFO ");
            exit(EXIT_FAILURE);
        }
        else
            printf("FIFO Created Successfully...\n");
    }

    while(strcmp(str,"end")!=0){
        fifo_write= open(argv[1],O_WRONLY);
        FD_SET(fifo_read, &readset);
        FD_SET(STDIN_FILENO, &readset);
        status = select(fifo_read+1, &readset, NULL, NULL, NULL);
        if(status==-1){
            perror("Error While Calling select() system call ");
            //exit(EXIT_FAILURE);   
        }
        if(FD_ISSET(STDIN_FILENO,&readset)){
            if(fifo_write<0)
                perror("\nError while writing on pipe ");
            else{
                printf("\nServer>> ");
                scanf("%s",str);
                write(fifo_write,str,strlen(str));
                close(fifo_write);
            }
        }
        fifo_read=open(argv[1],O_RDONLY);
        if(FD_ISSET(fifo_read,&readset)){
            if(fifo_read<0)
                perror("\nError while reading from pipe ");
            else{
                read(fifo_read,str,strlen(str));
                close(fifo_read);
                printf("\nJiya%s",str);
            }
        }
    }
    return 0;
}

client.c

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>
#include <sys/select.h>
int main(int argc,char *argv[]){
    int f,fifo_read,fifo_write,status;
    fd_set readset;
    FD_ZERO(&readset);
    char str[512]="start";
    
    while(strcmp(str,"end")!=0){
        fifo_write= open(argv[1],O_WRONLY);
        FD_SET(fifo_read, &readset);
        FD_SET(STDIN_FILENO, &readset);
        status = select(fifo_read+1, &readset, NULL, NULL, NULL);
        if(status==-1){
            perror("Error While Calling select() system call ");
            //exit(EXIT_FAILURE);   
        }
        if(FD_ISSET(fifo_read,&readset)){
            if(fifo_read<0)
                printf("\nError opening read pipe");
            else{
                read(fifo_read,str,strlen(str));
                close(fifo_read);
                printf("\n%s",str);
            }
        }
        fifo_read=open(argv[1],O_RDONLY);
        if(FD_ISSET(STDIN_FILENO,&readset)){
            if(fifo_write<0)
                printf("\nError opening write pipe");
            else{
                printf("\nClient>> ");
                scanf("%s",str);
                write(fifo_write,str,strlen(str));
                close(fifo_write);
            }
        }
    }
    return 0;
}

【问题讨论】:

    标签: c operating-system ipc


    【解决方案1】:

    我在下面发布了您的代码的一个工作的、稍微简化的版本。此代码从客户端中的 STDIN 读取,并通过 mkfifo 创建的管道将该输入发送到服务器。服务器从管道的末端循环读取并将读取的内容回显到 STDOUT。

    客户端输出:

    CLIENT > Message1
    CLIENT > Message2
    CLIENT > Yet another message.
    CLIENT > A fourth message, but this one is quite a bit longer than the first messages.
    CLIENT > end
    

    服务器输出:

    SERVER: Got 8 byte message: 'Message1'                                                                      
    SERVER: Got 8 byte message: 'Message2'                                                                      
    SERVER: Got 20 byte message: 'Yet another message.'                                                         
    SERVER: Got 77 byte message: 'A fourth message, but this one is quite a bit longer than the first messages.'
    EOF encountered...                                                                                          
    

    我将强调一些主要区别:

    • 我首先在服务器中通过mkfifo创建管道专用文件。然后我在服务器和客户端中打开特殊文件。这发生在循环之前,并且文件描述符在循环期间保持打开状态,而不是反复打开和关闭。
    • 客户端只写入管道,服务器只从管道读取。至少在 Linux 上,管道是单向的 (pipe man page: Pipes and FIFOs (also known as named pipes) provide a unidirectional interprocess communication channel. A pipe has a read end and a write end...) 在 Linux 上可以使用两个管道进行双向通信,即使在支持双向管道的系统上,仍然支持使用两个管道并且更便携。
    • 调用read 时,应使用str 缓冲区的总大小,而不是strlen(str)。此外,str 应在读取之间清除,以免包含旧数据(memset 在新代码中)。
    • 我使用了fgets 而不是scanf
    • 服务器打开FIFO后,我unlink文件,使其自动从文件系统中删除。底层文件对象仍然存在,直到使用它的进程终止。这是可选的。

    服务器:

    #include <fcntl.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/select.h>
    #include <sys/stat.h>
    #include <unistd.h>
    
    int main(int argc, char *argv[]) {
        int fifo_read = -1;
        char str[512]= "";
    
        fd_set readset;
        FD_ZERO(&readset);
    
        if (argc != 2) {
            printf("Usage: %s FIFO_NAME\n", argv[0]);
            exit(EXIT_FAILURE);
        }
    
        /* Server will make the FIFO, both sides will open it */
        if (mkfifo(argv[1], S_IRWXU) == -1) {
            perror("mkfifo()");
            exit(EXIT_FAILURE);
        }
    
        if ((fifo_read = open(argv[1], O_RDONLY)) == -1) {
            perror("open()");
            exit(EXIT_FAILURE);
        }
    
        if (unlink(argv[1]) == -1) {
            perror("unlink()");
            exit(EXIT_FAILURE);
        }
    
        while (1) {
            FD_SET(fifo_read, &readset);
            if (select(fifo_read + 1, &readset, NULL, NULL, NULL) == -1) {
                perror("select()");
                exit(EXIT_FAILURE);
            }
    
            if (FD_ISSET(fifo_read, &readset)) {
                ssize_t bytes_read;
                memset(str, 0, sizeof(str));
                bytes_read = read(fifo_read, str, sizeof(str));
                if (bytes_read == -1) {
                    perror("read()");
                    exit(EXIT_FAILURE);
                }
                else if (bytes_read == 0) {
                    printf("EOF encountered...\n");
                    break;
                }
                printf("SERVER: Got %ld byte message: '%s'\n", bytes_read, str);
            }
        }
    
        if (close(fifo_read) == -1) {
            perror("close()");
            exit(EXIT_FAILURE);
        }
    
        exit(EXIT_SUCCESS);
    }
    

    客户:

    #include <fcntl.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/select.h>
    #include <sys/stat.h>
    #include <unistd.h>
    
    int main(int argc, char *argv[]) {
        int fifo_write = -1;
        char str[512] = "";
    
        fd_set readset;
        FD_ZERO(&readset);
    
        if (argc != 2) {
            printf("Usage: %s FIFO_NAME\n", argv[0]);
            exit(EXIT_FAILURE);
        }
    
        if ((fifo_write = open(argv[1], O_WRONLY)) == -1) {
            perror("open()");
            exit(EXIT_FAILURE);
        }
    
        while (1) {
            printf("CLIENT > ");
            fflush(stdout);
    
            FD_SET(STDIN_FILENO, &readset);
            if (select(STDIN_FILENO + 1, &readset, NULL, NULL, NULL) == -1) {
                perror("select()");
                exit(EXIT_FAILURE);
            }
            if (FD_ISSET(STDIN_FILENO, &readset)) {
                memset(str, 0, sizeof(str));
                if (!fgets(str, sizeof(str), stdin)) {
                    printf("fgets() failed to read a line.\n");
                    exit(EXIT_FAILURE);
                }
    
                // See https://stackoverflow.com/questions/2693776/removing-trailing-newline-character-from-fgets-input
                str[strcspn(str, "\r\n")] = 0;
    
                if (strcmp(str, "end") == 0) {
                    break;
                }
                if (write(fifo_write, str, strlen(str)) == -1) {
                    perror("write()");
                    exit(EXIT_FAILURE);
                }
            }
        }
    
        if (close(fifo_write) == -1) {
            perror("close()");
            exit(EXIT_FAILURE);
        }
    
        exit(EXIT_SUCCESS);
    }
    

    【讨论】:

      【解决方案2】:

      这里我得到了解决方案:)

      服务器.c

      #include <stdio.h>
      #include <unistd.h>
      #include <fcntl.h>
      #include <stdlib.h>
      #include <sys/select.h>
      #include <string.h>
      #include <sys/stat.h>
      #include <errno.h>
      
      int main(int argc, char *argv[])
      {
          if(argc != 2){
              printf("\nError: %s required argument [Fifo Name]\n\n",argv[0]);
              exit(EXIT_FAILURE);
          }
          int fd,wr,rd,ret;
          fd_set readset;
          if(mkfifo(argv[1],S_IRWXU)==-1){
              if(errno!=EEXIST)
                  perror("Error unable to create FIFO ");
              else
                  perror("Error unable to create FIFO ");
              exit(EXIT_FAILURE);
          }
          else
              printf("FIFO created Successfully!\n\n");
          fd = open(argv[1],O_RDWR);
          if(fd==-1){
              perror("Error Failed to open fifo\n");
              exit(EXIT_FAILURE);
          }
          while(!0){
              FD_ZERO(&readset);
              FD_SET(fd,&readset);
              FD_SET(STDIN_FILENO,&readset);
              sleep(1);
              ret = select(fd+1,&readset,NULL,NULL,NULL);
              if(ret==-1){
                  perror("Error select() ");
                  exit(EXIT_FAILURE);
              }
              char str[512]="";
              if(FD_ISSET(STDIN_FILENO,&readset)){
                  //fprintf(stderr, ">> ");
                  rd = read(STDIN_FILENO,str,sizeof(str));
                  if(rd==-1){
                      perror("Error while reading from fifo");
                      exit(EXIT_FAILURE);
                  }
                  char temp[512]="Server :: ";
                  strcat(temp,str);
                  wr = write(fd,temp,sizeof(temp));
                  if(wr==-1){
                      perror("Error while writing to fifo ");
                      exit(EXIT_FAILURE);
                  }
                  continue;
              }
              if(FD_ISSET(fd,&readset)){
                  rd = read(fd,str,sizeof(str));
                  if(rd==-1){
                      perror("Error while reading from fifo");
                      exit(EXIT_FAILURE);
                  }else if(rd==0)
                      continue;
                  //fprintf(stderr,"P2: %s\n",str);
                  printf("%s\n",str);
                  //write(STDOUT_FILENO,str,sizeof(str));
              }
          }
      }
      

      client.c

      #include <stdio.h>
      #include <unistd.h>
      #include <fcntl.h>
      #include <stdlib.h>
      #include <sys/select.h>
      #include <string.h>
      #include <sys/stat.h>
      #include <errno.h>
      
      int main(int argc, char *argv[])
      {
          if(argc != 2){
              printf("\nError: %s required argument [Fifo Name]\n\n",argv[0]);
              exit(EXIT_FAILURE);
          }
          int fd,wr,rd,ret;
          fd_set readset;
          fd = open(argv[1],O_RDWR);
          if(fd==-1){
              perror("Error Failed to open fifo\n");
              exit(EXIT_FAILURE);
          }
          while(!0){
              FD_ZERO(&readset);
              FD_SET(fd,&readset);
              FD_SET(STDIN_FILENO,&readset);
              sleep(2);
              ret = select(fd+1,&readset,NULL,NULL,NULL);
              if(ret==-1){
                  perror("Error select() ");
                  exit(EXIT_FAILURE);
              }
              char str[512]="";
              if(FD_ISSET(fd,&readset)){
                  rd = read(fd,str,sizeof(str));
                  if(rd==-1){
                      perror("Error while reading from fifo");
                      exit(EXIT_FAILURE);
                  }else if(rd==0)
                      continue;
                  //fprintf(stderr,"P2: %s\n",str);
                  printf("%s\n",str);
                  //write(STDOUT_FILENO,str,sizeof(str));
              }
              if(FD_ISSET(STDIN_FILENO,&readset)){
                  //fprintf(stderr, ">> ");
                  rd = read(STDIN_FILENO,str,sizeof(str));
                  if(rd==-1){
                      perror("Error while reading from fifo");
                      exit(EXIT_FAILURE);
                  }
                  char temp[512]="Client :: ";
                  strcat(temp,str);
                  wr = write(fd,temp,sizeof(temp));
                  if(wr==-1){
                      perror("Error while writing to fifo ");
                      exit(EXIT_FAILURE);
                  }
                  continue;
              }
          }
      }
      
      

      输出是:

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-10-04
        • 2015-06-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多