如何通过 select 系统调用使用 FIFO 从服务器与客户端聊天?
How to chat with client from a server using FIFO with select system call?
你好,我正在尝试制作这样一个程序,其中两个 fds stdin
和 fifo
由 select()
监控并相互通信。
select()
将监视 fifo
准备好读取或 stdin
.
server.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;
}
我在下面发布了您的代码的一个有效的、稍微简化的版本。此代码从客户端中的 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
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);
}
这里我找到了解决方案:)
server.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;
}
}
}
输出为:
你好,我正在尝试制作这样一个程序,其中两个 fds stdin
和 fifo
由 select()
监控并相互通信。
select()
将监视 fifo
准备好读取或 stdin
.
server.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;
}
我在下面发布了您的代码的一个有效的、稍微简化的版本。此代码从客户端中的 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
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);
}
这里我找到了解决方案:)
server.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;
}
}
}
输出为: