我正在使用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-BaptisteYuè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-BaptisteYunè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;
}
答案 0 :(得分:4)
您必须使用select
。你需要在两个通道上等待:来自管道的东西或来自stdin的东西,但你永远不知道要读哪一个。 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
}
}