C FIFO - 如何在等待客户端请求时读取服务器的标准输入
C FIFO's - How to read server's stdin while waiting for client requests
我正在使用 C 中的 linux 命名管道实现一个简单的 ipc 系统。
我有这个服务器代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "external/paths.h"
#include "external/sv.h"
#include "external/tags.h"
int main(int argc, char *argv[])
{
int fd, bytes_read;
char request[200];
// create fifo
mknod(FIFO_SERVER, S_IFIFO | 0666, 0);
puts("Servidor initialized.\nWaiting for client requests.");
// open created fifo
fd = open(FIFO_SERVER, O_RDONLY);
while(1)
{
if( (bytes_read = read(fd, request, LEN_CL_REQUEST)) == -1 )
perror("error read()");
if(bytes_read == 0)
continue;
if(bytes_read > 0)
{
printf("Request read: %s\n", request);
// answer back
}
}
close(fd);
unlink(FIFO_SERVER);
return 0;
}
我忽略了客户端,因为我的问题只与服务器有关。通信工作正常,我可以读取来自客户的请求并且我可以回答它们。现在,假设我想在按下 'Q' 键时随时退出服务器。我不能这样做,因为我的代码阻塞在 read
语句上等待另一个客户端请求,所以我无法读取 stdin
..
这样的事情可以做吗?我正在考虑非阻塞 read
语句并尝试读取 stdin
几秒钟,然后再次检查传入请求..我一直在搜索但我还没有找到任何类似的东西。
更新:
我遵循了 Jean-Baptiste Yunès 的方法,但事实证明 select
只是检测 fifo 事件,我真的不知道为什么。
这是我正在测试的代码:
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
int result, fd, maxDescriptor;
char input[20], texto[100];
mknod("fifo", S_IFIFO | 0666, 0);
fd = open("fifo", O_RDWR); // RDWR to avoid EOF return to select
fd_set readset;
FD_ZERO(&readset);
FD_SET(fileno(stdin), &readset);
FD_SET(fd, &readset);
maxDescriptor = fileno(stdin) > fd ? fileno(stdin) : fd;
while(1)
{
result = select(maxDescriptor+1, &readset, NULL, NULL, NULL);
if(result == -1)
perror("select()");
else if(result)
{
puts("data available.");
if( FD_ISSET(fileno(stdin), &readset) )
{
scanf("%s", input);
printf("%s\n", input);
if( strcmp(input, "Q") == 0 )
break;
}
if( FD_ISSET(fd, &readset) )
{
read(fd, texto, 100);
printf("lido:\n%s\n", texto);
}
}
else
puts("no data.");
}
unlink("fifo");
return 0;
}
更新 2:
正如 Jean-Baptiste Yunès 指出的那样,需要重置 fd_set 因为它不会自动重置。
这是最终的工作代码:
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
int result, fd, maxDescriptor;
char input[20], texto[100];
mknod("fifo", S_IFIFO | 0666, 0);
fd = open("fifo", O_RDWR); // RDWR to avoid EOF return to select
fd_set readset;
FD_ZERO(&readset);
FD_SET(fileno(stdin), &readset);
FD_SET(fd, &readset);
maxDescriptor = fileno(stdin) > fd ? fileno(stdin) : fd;
while(1)
{
result = select(maxDescriptor+1, &readset, NULL, NULL, NULL);
if(result == -1)
perror("select()");
else if(result)
{
puts("data available.");
if( FD_ISSET(fileno(stdin), &readset) )
{
scanf("%s", input);
printf("%s\n", input);
if( strcmp(input, "Q") == 0 )
break;
}
if( FD_ISSET(fd, &readset) )
{
read(fd, texto, 100);
printf("lido:\n%s\n", texto);
}
FD_SET(fileno(stdin), &readset);
FD_SET(fd, &readset);
}
else
puts("no data.");
}
unlink("fifo");
return 0;
}
您必须使用 select
。您需要在两个通道上等待:管道中的某些内容或标准输入中的某些内容,但您永远不知道要读取哪个。 select 的目的是让您的进程在任何通道上等待读取或写入。
fd_set readset;
FD_ZERO(&readset); // empty set of descriptor to select on
FD_SET(fd, &readset); // add the pipe
FD_SET(stdin, &readset); // add stdin
result = select(fd + 1, &readset, NULL, NULL, NULL); // now wait for something to read on at least one channel (pipe or stdin)
if (result>0) {
if (FD_ISSET(fd, &readset)) { // test for pipe availability
// now read the pipe
}
if (FD_ISSET(stdin, &readset)) { // test for stdin availability
// now read stdin
}
}
我正在使用 C 中的 linux 命名管道实现一个简单的 ipc 系统。
我有这个服务器代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "external/paths.h"
#include "external/sv.h"
#include "external/tags.h"
int main(int argc, char *argv[])
{
int fd, bytes_read;
char request[200];
// create fifo
mknod(FIFO_SERVER, S_IFIFO | 0666, 0);
puts("Servidor initialized.\nWaiting for client requests.");
// open created fifo
fd = open(FIFO_SERVER, O_RDONLY);
while(1)
{
if( (bytes_read = read(fd, request, LEN_CL_REQUEST)) == -1 )
perror("error read()");
if(bytes_read == 0)
continue;
if(bytes_read > 0)
{
printf("Request read: %s\n", request);
// answer back
}
}
close(fd);
unlink(FIFO_SERVER);
return 0;
}
我忽略了客户端,因为我的问题只与服务器有关。通信工作正常,我可以读取来自客户的请求并且我可以回答它们。现在,假设我想在按下 'Q' 键时随时退出服务器。我不能这样做,因为我的代码阻塞在 read
语句上等待另一个客户端请求,所以我无法读取 stdin
..
这样的事情可以做吗?我正在考虑非阻塞 read
语句并尝试读取 stdin
几秒钟,然后再次检查传入请求..我一直在搜索但我还没有找到任何类似的东西。
更新:
我遵循了 Jean-Baptiste Yunès 的方法,但事实证明 select
只是检测 fifo 事件,我真的不知道为什么。
这是我正在测试的代码:
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
int result, fd, maxDescriptor;
char input[20], texto[100];
mknod("fifo", S_IFIFO | 0666, 0);
fd = open("fifo", O_RDWR); // RDWR to avoid EOF return to select
fd_set readset;
FD_ZERO(&readset);
FD_SET(fileno(stdin), &readset);
FD_SET(fd, &readset);
maxDescriptor = fileno(stdin) > fd ? fileno(stdin) : fd;
while(1)
{
result = select(maxDescriptor+1, &readset, NULL, NULL, NULL);
if(result == -1)
perror("select()");
else if(result)
{
puts("data available.");
if( FD_ISSET(fileno(stdin), &readset) )
{
scanf("%s", input);
printf("%s\n", input);
if( strcmp(input, "Q") == 0 )
break;
}
if( FD_ISSET(fd, &readset) )
{
read(fd, texto, 100);
printf("lido:\n%s\n", texto);
}
}
else
puts("no data.");
}
unlink("fifo");
return 0;
}
更新 2:
正如 Jean-Baptiste Yunès 指出的那样,需要重置 fd_set 因为它不会自动重置。
这是最终的工作代码:
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
int result, fd, maxDescriptor;
char input[20], texto[100];
mknod("fifo", S_IFIFO | 0666, 0);
fd = open("fifo", O_RDWR); // RDWR to avoid EOF return to select
fd_set readset;
FD_ZERO(&readset);
FD_SET(fileno(stdin), &readset);
FD_SET(fd, &readset);
maxDescriptor = fileno(stdin) > fd ? fileno(stdin) : fd;
while(1)
{
result = select(maxDescriptor+1, &readset, NULL, NULL, NULL);
if(result == -1)
perror("select()");
else if(result)
{
puts("data available.");
if( FD_ISSET(fileno(stdin), &readset) )
{
scanf("%s", input);
printf("%s\n", input);
if( strcmp(input, "Q") == 0 )
break;
}
if( FD_ISSET(fd, &readset) )
{
read(fd, texto, 100);
printf("lido:\n%s\n", texto);
}
FD_SET(fileno(stdin), &readset);
FD_SET(fd, &readset);
}
else
puts("no data.");
}
unlink("fifo");
return 0;
}
您必须使用 select
。您需要在两个通道上等待:管道中的某些内容或标准输入中的某些内容,但您永远不知道要读取哪个。 select 的目的是让您的进程在任何通道上等待读取或写入。
fd_set readset;
FD_ZERO(&readset); // empty set of descriptor to select on
FD_SET(fd, &readset); // add the pipe
FD_SET(stdin, &readset); // add stdin
result = select(fd + 1, &readset, NULL, NULL, NULL); // now wait for something to read on at least one channel (pipe or stdin)
if (result>0) {
if (FD_ISSET(fd, &readset)) { // test for pipe availability
// now read the pipe
}
if (FD_ISSET(stdin, &readset)) { // test for stdin availability
// now read stdin
}
}