基于此answer,我正在模拟两位作家和一位读者。
所以,我创建了两个管道,我给每个管道写了一个实际的字符串,一个字符串告诉读者他已经完成了这个管道。
但是,它只会读取第一个字符串,有时还会读取第二个管道的结束字符串。
我错过了什么?
reader.c
int main() {
int w_no = 2;
int fd[w_no];
char * myfifo[w_no];
fill_names(myfifo, w_no);
print_names(myfifo, w_no);
struct pollfd fdtab[w_no];
int done[w_no];
/* create the FIFO (named pipe) */
int i;
for (i = 0; i < w_no; ++i) {
//fd[i] = open(myfifo[i], O_RDONLY);
while( (fd[i] = open(myfifo[i], O_RDONLY)) == -1);
fdtab[i].fd = fd[i];
fdtab[i].events = POLLIN;
fdtab[i].revents = 0;
done[i] = 0;
}
char buffer[1024];
ssize_t bytes;
printf("Edw prin\n");
while(not_all_done(done, w_no)) {
int retpoll = poll(fdtab, w_no, 300);
if(retpoll != 0) {
if (retpoll == -1) {
perror("poll");
break;
}
for(i = 0; i < w_no; ++i) {
if(fdtab[i].revents & POLLIN) {
printf("Edw %d %d %d %d\n", i, retpoll, fdtab[i].revents, POLLIN);
//read the written pipe
while((bytes = read(fdtab[i].fd, buffer, sizeof(buffer))) > 0)
printf("Read |%s| %d %d %d\n", buffer, retpoll, fdtab[i].revents, POLLIN);
if(!strcmp(buffer, "++"))
done[i] = 1;
}
}
} else if (retpoll == 0) {
/* the poll has timed out, nothing can be read or written */
printf("timeout from writer\n");
break;
}
}
for (i = 0; i < w_no; ++i) {
close(fd[i]);
}
free_names(myfifo, w_no);
return 0;
}
writer.c
int main() {
int w_no = 2;
int fd[w_no];
char * myfifo[w_no];
fill_names(myfifo, w_no);
print_names(myfifo, w_no);
/* create the FIFO (named pipe) */
int i;
int bytes;
for (i = 0; i < w_no; ++i) {
mkfifo(myfifo[i], 0666);
fd[i] = open(myfifo[i], O_WRONLY);
while( (bytes = write(fd[i], "Hi+", sizeof("Hi+"))) == 3);
printf("wrote %d bytes, %d\n", bytes, sizeof("Hi+"));
while( (bytes = write(fd[i], "++", sizeof("++"))) == 2);
printf("wrote %d bytes, %d\n", bytes, sizeof("++"));
}
for (i = 0; i < w_no; ++i) {
close(fd[i]);
unlink(myfifo[i]);
}
free_names(myfifo, w_no);
return 0;
}
示例输出:
/tmp/myfifo_0
/tmp/myfifo_0
/tmp/myfifo_1
/tmp/myfifo_1
wrote 4 bytes, 4
wrote 3 bytes, 3
wrote 4 bytes, 4
Edw prin
wrote 3 bytes, 3
Edw 0 2 17 1
Read |Hi+| 2 17 1
Edw 1 2 1 1
Read |Hi+| 2 1 1
^C
修改
当Hi+
字符串到达时,bytes
的值为7。
我要发送的结束字符串为++
,但它无法读取。
EDIT_2
char* concat(char *s1, char *s2) {
char *result = malloc(strlen(s1) + strlen(s2) + 1); //+1 for the null-terminator
//in real code you would check for errors in malloc here
strcpy(result, s1);
strcat(result, s2);
return result;
}
void fill_names(char* f[], int n) {
int i = 0;
char * buf = "/tmp/myfifo_";
char str[15];
for (; i < n; ++i) {
sprintf(str, "%d", i);
f[i] = concat(buf, str);
}
}
观
也许作者在从中读取数据之前关闭和取消链接管道?如果是这样,我该怎么做才能防止这种情况?
如果在此之前加sleep(10)
,它就不会&#39;改变行为,它只会读取两个第一个字符串,但它会花费更多时间然后它会挂起(因为它等待结束字符串)。
EDIT_3
我还有一个掌握读者和作家的main.c。
答案 0 :(得分:1)
您的字符串写法存在问题:
while( (bytes = write(fd[i], "Hi+", sizeof("Hi+"))) == 3);
printf("wrote %d bytes, %d\n", bytes, sizeof("Hi+"));
while( (bytes = write(fd[i], "++", sizeof("++"))) == 2);
printf("wrote %d bytes, %d\n", bytes, sizeof("++"));
这里发送7个字节:H i + \0 + + \0
,因为字符串litteral的sizeof()
包含空终止符。
顺便说一下,只要写入3个字节,while((bytes=write(...))==3)
就会循环。这不会发生在这里,因为你的写作也是null终结符。但最好删除封闭的while
。
由于管道是一个流,没有任何保证您将收到两个不同读取的字节。实际上,您的所有解释和日志都表明您一次接收所有7个字节。
但是,您使用printf("Read |%s| %d %d %d\n"...)
打印内容:打包包含&#39; \ 0&#34;的字符串的结果。未定义。在您的情况下,打印的字符串被截断。所以只有&#34;嗨+&#34;打印但是&#34; \ 0 ++&#34;仍隐藏在缓冲区中。
顺便说一句,while((bytes = read(...)) > 0)
可以多次循环和打印。这本身不是问题。只是如果作者及时发送数据,连续读取可能会暂时锁定其他管道的读数。通常在极化程序中,人们更喜欢从每个准备好的管道中读取一点点。
检查结束字符串
if(!strcmp(buffer, "++"))
done[i] = 1;
在大多数情况下,可能不会成功。您不确定一方的写入是否会导致另一方的读取。所以你的&#34; ++&#34; string不一定在缓冲区的开头。它可以在缓冲区中的任何位置,因此您必须搜索它。它甚至可以在两次连续读取之间分开。
顺便说一下,read()
可能只找到部分数据(例如:&#34; i +&#34;),而不是终止空值。如果您尝试打印缓冲区,假设其中包含有效字符串,则存在缓冲区溢出的风险。
<强>建议:强>
如果您的命名管道用于处理文本数据,我建议在您要发送的每组数据的末尾添加'\n'
,并将字符串写入管道而不终止null:
bytes = write(fd[i], "Hi+\n", sizeof("Hi+\n")-1);
然后,当您阅读时,您可以像字符串一样管理缓冲区:始终添加尾随0:
bytes = read(fdtab[i].fd, buffer, sizeof(buffer)-1); // leave a byte for terminator
if (bytes>0) {
buffer[bytes]=0; // end of string.
// process the string in the buffer
}
else if (bytes==0) {
done[i]=1;
}
最后,为了识别您的结束命令,假设您已将其发送为&#34; ++ \ n&#34;有三种可能性:
if (strncmp(buffer,"++\n",3)==0 /* it's at the beginning of the buffer */
|| strstr(buffer, "\n++\n") ) /* it's in the middle but not a subpart and preceded by a packet separator */
done[i]=1;
但是你还必须检查两次读取之间的分裂。这更精致,但我确定你找到了办法; - )
答案 1 :(得分:0)
尝试同时打印您在阅读器程序中读取的字节数。我的猜测是你已经阅读了比你想象的更多的字节,但是在打印字符串时你只能获得第一个终止零之前的字节。
您是否打算在命名管道上发送终止零?也许最好发送其他类似换行符的东西?