Reputation: 13
I have a problem with the interaction of two programs:
Program I has to periodically check if there is data coming in from Program II. This should be non-blocking.
Program II can run in multiple instances and is therefore parallel. It sends some data to Program I and then exits. The exact order of the data is not that important, it would be enough if data that is sent 1 second after another set of data is also received ~1 second later.
My first approach was using FIFOs: "Server"
int main()
{
int fd;
unlink("/tmp/ctl_fifo");
if (mkfifo("/tmp/ctl_fifo", 0666) == -1)
{
printf("Error creating FIFO\n");
}
while (true)
{
if ((fd = open("/tmp/ctl_fifo", O_RDONLY | O_NONBLOCK)) < 0)
{
printf("Error opening FIFO\n");
}
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 2000;
fd_set set;
FD_ZERO(&set);
FD_SET(fd, &set);
select(fd + 1, &set, NULL, NULL, &timeout);
if (FD_ISSET(fd, &set))
{
char buf[20];
int r = read(fd,buf, sizeof(buf));
printf("%s\n", buf);
}
sleep(1);
}
return 0;
}
"client"
int main()
{
while (true)
{
int fd;
fd = open("/tmp/ctl_fifo", O_WRONLY);
if (fd < 0)
{
printf("Error opening fifo\n");
}
char buf[] = "Blablubb";
write(fd, buf, sizeof(buf));
sleep(0.1);
}
return 0;
}
The problem with this is that the Data can get scrambled. The output looks something like this:
Blablubb
ablubb
ubb
b
Blablubb
...
My questions are: What do I do wrong? Is there a way to ensure that the data does not get scrambled? Or is there a complete different way of handling this Problem?
Upvotes: 1
Views: 147
Reputation: 15642
Firstly, you have a serious resource leak; every time your loop runs you open
a new stream and at the end of that loop iteration that stream leaks, floating away into thin air as though it never existed... The problem is it did exist, and you didn't close
it.
Secondly, when your streams opened using O_NONBLOCK
, you don't need to use select
.
read
will return (an error value) immediately when there's nothing to read, and the stream is opened using O_NONBLOCK
.
int r = read(fd,buf, sizeof(buf));
printf("%s\n", buf);
read
returns an ssize_t
(the signed equivalent to size_t
). So int r
should be ssize_t r
.
Moving on (from the previous paragraph) to printf
...
printf("%s\n", buf); // THIS IS WRONG!
You need to check r
, for a start. It's entirely possible that no bytes were received, and buf
contains pure garbage. It's also possible that two writes could be merged (and you only end up printing one of them).
I recommend for simplicity that you receive (and print) a single byte at a time, except for when you receive '\0'
; print '\n
' instead of '\0'
. Like this:
int fd = open("/tmp/ctl_fifo", O_RDONLY | O_NONBLOCK);
if (fd < 0)
{
printf("Error opening FIFO\n");
}
for (;;)
{
unsigned char c;
ssize_t r = read(fd, &c, 1);
if (r < 0 && errno == EAGAIN) {
/* read sets EAGAIN when no data is available on otherwise valid
* non-blocking streams, and returns -1... so we continue looping
* when that happens */
continue;
}
if (r <= 0) {
/* Any value less than zero without errno == EAGAIN should terminate
* ... and r == 0 means EOF */
break;
}
putchar(c == '\0' ? '\n' : c);
}
close(fd);
Upvotes: 2