如何使用带有选择系统调用的 FIFO 从服务器与客户端聊天?
Posted
技术标签:
【中文标题】如何使用带有选择系统调用的 FIFO 从服务器与客户端聊天?【英文标题】:How to chat with client from a server using FIFO with select system call? 【发布时间】:2021-02-16 15:56:00 【问题描述】:您好,我正在尝试制作这样一个程序,其中两个 fds stdin
和 fifo
由 select()
监控并相互通信。
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;
【问题讨论】:
【参考方案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://***.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;
输出是:
【讨论】:
以上是关于如何使用带有选择系统调用的 FIFO 从服务器与客户端聊天?的主要内容,如果未能解决你的问题,请参考以下文章