更新20-12-2014:此问题已解决,请参阅问题的底部以了解正常工作的代码。
有四个客户端处理某些数据,然后通过命名管道(FIFO)将其传递给服务器进程。
在gdb之外运行服务器时(不是踩到gdb也会出现同样的问题)只读取一个管道。选择返回1,FD_ISSET仅响应一个管道(并且在执行期间它保持相同的管道)。
查看/ proc / [PID] / {fd,fdinfo}表示其他管道仍然处于打开状态并且尚未读取。 pos
中的fdinfo
字段为0)。
我需要更改以交错的方式从所有四个管道中读取?
为了模拟客户端,我使用一个12MByte随机文件cat
到命名管道上。
使用以下命令生成随机文件:
dd if=/dev/urandom of=test.bin bs=1024 count=$((1024*12))
然后执行(每个在一个单独的终端中按以下顺序执行)
terminal 1:
./server.out
terminal 2:
cat test.bin > d0
terminal 3:
cat test.bin > d1
terminal 4:
cat test.bin > d2
terminal 5:
cat test.bin > d3
生成文件
server:
gcc server.c -o server.out -g -D _DEFAULT_SOURCE -Wall --std=c11
来源
客户端被称为加密狗。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/select.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#define NR_OF_DONGLES 4
int do_something(int fd);
int main()
{
fd_set read_fd_set;
FD_ZERO(&read_fd_set);
int dongles[NR_OF_DONGLES];
/*Create FIFO */
for(int i = 0; i < NR_OF_DONGLES; i++)
{
char name[255];
snprintf(name, sizeof(name), "d%d", i);
if(mkfifo(name, 0666) == -1)
{
fprintf(stderr, "Failed to create fifo %s \t Error: %s", name, name);
exit(EXIT_FAILURE);
}
int dongle = open(name, O_RDONLY);
if(dongle > 0)
{
fprintf(stderr,"set dongle %s\n", name);
FD_SET(dongle, &read_fd_set);
dongles[i] = dongle;
}
else
{
fprintf(stderr, "failed to open: %s\nerror: %s\n", name, strerror(errno));
exit(EXIT_FAILURE);
}
}
int closed = 0;
int isset[NR_OF_DONGLES];
memset(isset, 0, sizeof(isset));
while(closed < NR_OF_DONGLES)
{
int active;
if((active = select (FD_SETSIZE , &read_fd_set, NULL,NULL,NULL)) < 0)
{
fprintf(stderr, "select failed\n errno: %s\n",strerror(errno));
exit(EXIT_FAILURE);
}
fprintf(stderr, "active devices %i\n", active);
for(int i = 0; i < NR_OF_DONGLES; ++i)
{
int dongle = dongles[i];
if(FD_ISSET(dongle, &read_fd_set))
{
isset[i] += 1;
int size = do_something(dongle);
fprintf(stderr, "round %i \tdongle %i \tread %i bytes\n", isset[i],i, size);
if(size == 0)
{
if(close(dongle) == -1)
{
fprintf(stderr,"Could not close dongle %i\nError: %s\n",
i,strerror(errno));
}
closed += 1;
fprintf(stderr, "closed dongle %i \t number of closed dongles %i\n",
i, closed);
FD_CLR(dongle, &read_fd_set);
}
}
}
}
exit(EXIT_SUCCESS);
}
#define BLOCK_SIZE (8*1024)
/*
* If the size is zero we reached the end of the file and it can be closed
*/
int do_something(int fd)
{
int8_t buffer[BLOCK_SIZE];
ssize_t size = read(fd, buffer, sizeof(buffer));
if(size > 0)
{
//Process read data
}
else if(size == -1)
{
fprintf(stderr, "reading dongle failed\nerrno: %s", strerror(errno));
return -1;
}
return size;
}
read_fd_set
)。
源代码
while(closed < number_of_dongles)
{
/*Reinitialize watchlist of file descriptors.*/
FD_ZERO(&read_fd_set);
for(int i = 0; i < number_of_dongles; i++)
{
int dongle = dongles[i];
/*if fd == -1 the pipe has been closed*/
if(dongle != -1)
{
FD_SET(dongle, &read_fd_set);
}
}
int active = select (FD_SETSIZE , &read_fd_set, NULL,NULL,NULL);
if(active < 0)
{
fprintf(stderr, "select failed\n errno: %s\n",strerror(errno));
exit(EXIT_FAILURE);
}
//fprintf(stderr, "active devices %i\n", active);
for(int i = 0; i < number_of_dongles; ++i)
{
int dongle = dongles[i];
/*Check if the current dongle fd has data in the FIFO*/
if(FD_ISSET(dongle, &read_fd_set))
{
isset[i] += 1;
int size = transfer_dongle_data(dongle);
// fprintf(stderr, "round %i \tdongle %i \tread %i bytes\n", isset[i],i, size);
if(size == 0)
{
if(close(dongle) == -1)
{
fprintf(stderr,"Could not close dongle %i\nError: %s\n",
i,strerror(errno));
}
closed += 1;
fprintf(stderr, "closed dongle %i \t number of closed dongles %i\n",
i, closed);
FD_CLR(dongle, &read_fd_set); //could be removed
/*notify that the pipe is closed*/
dongles[i] = -1;
}
}
}
}
答案 0 :(得分:0)
您可以尝试通过strace
(Solaris上的truss
,FreeBSD上的ktrace/kdump
)运行您的代码。对我来说,它停留在open("d0", O_RDONLY)
。因此,服务器之前不会创建所有管道(其他管道最有可能由cat
创建)。
if(dongle)...
不正确:如果失败,open()
返回-1,而不是0。
因此,我认为您的程序无法使用您期望的文件(只有一个管道正确打开)。
与select()
使用相关的另一个问题。您应该在每次调用read_fd_set
之前重新初始化select()
,因为在每个select()
调用之后,只有描述符,其中有数据被标记,其他的被清除。
答案 1 :(得分:0)
经过一些修补,我明白了。在我的MacOSX open(...)
上会阻塞,直到FIFO上确实存在某些东西。考虑下面的程序;一旦开始将数据输入d0,d1等,它就会起作用。但在此之前,该计划的输出仅为:
Creating dongle d0
Creating dongle d1
Creating dongle d2
Creating dongle d3
Opening dongle d0
所以我拉出了open()
的联机帮助页,然后看到了 - O_NONBLOCK
。添加后,以下代码就像魅力一样。仅供参考,POSIX并不是说打开FIFO应该阻止,但我发现了一些实现的评论。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <stdarg.h>
#include <errno.h>
#include <sys/select.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#define NR_OF_DONGLES 4
#define BLOCK_SIZE (8*1024)
// Prints error and stops program
int error(char const *fmt, ...) {
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
exit(1);
}
// Alternative for printf() that flushes stdout
void msg(char const *fmt, ...) {
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
fflush(stdout);
}
// Reads an open readable fd
int do_something(int fd) {
int8_t buffer[BLOCK_SIZE];
ssize_t size = read(fd, buffer, sizeof(buffer));
if(size > 0)
msg("Got data from fd %d, length %ld\n", fd, size);
else if (size == -1)
error("Reading dongle fd %d failed: %s\n", fd, strerror(errno));
else
msg("Dongle with fd %d signals EOF\n", fd);
return size;
}
int main() {
int dongles[NR_OF_DONGLES];
// Create the dongles, open, add to list of open fd's
for (int i = 0; i < NR_OF_DONGLES; i++) {
char name[255];
snprintf(name, sizeof(name), "d%d", i);
msg("Creating dongle %s\n", name);
if (mkfifo(name, 0666) == -1 && errno != EEXIST)
error("Failed to create fifo %s: %s\n", name, strerror(errno));
}
for (int i = 0; i < NR_OF_DONGLES; i++) {
char name[255];
snprintf(name, sizeof(name), "d%d", i);
msg("Opening dongle %s\n", name);
/* ****************************************
* Here it is, first test was with
* int fd = open(name, O_RDONLY);
* which blocked at the open() call
*******************************************/
int fd = open(name, O_RDONLY | O_NONBLOCK);
if (fd < 0)
error("Cannot open dongle %s: %s\n", name, strerror(errno));
dongles[i] = fd;
}
int closed = 0;
while (closed < NR_OF_DONGLES) {
msg("Closed dongles so far: %d\n", closed);
// Add dongle fd's to select set, unless the fd is already closed
// which is indicated by fd == -1
fd_set read_fd_set;
FD_ZERO(&read_fd_set);
for (int i = 0; i < NR_OF_DONGLES; i++)
if (dongles[i] > 0)
FD_SET(dongles[i], &read_fd_set);
// Wait for readable fd's
int active;
if ( (active = select (FD_SETSIZE , &read_fd_set, 0, 0, 0)) < 0 )
error("Select failure: %s\n", strerror(errno));
msg("Active dongles: %d\n", active);
for (int fd = 0; fd < FD_SETSIZE; fd++)
if (FD_ISSET(fd, &read_fd_set)) {
msg("Fd %d is readable\n", fd);
int size = do_something(fd);
if (!size) {
// Fd signals EOF. Close dongle, remove from array
// of open fd's by setting to -1
msg("Fd %d signals EOF\n", fd);
if (close(fd) < 0)
error("Failure to close fd %d: %s\n",
fd, strerror(errno));
for (int i = 0; i < NR_OF_DONGLES; i++)
if (dongles[i] == fd)
dongles[i] = 0;
// Update closed fd counter
closed++;
}
}
}
exit(0);
}