Consider the following code:
writer.c
mkfifo("/tmp/myfifo", 0660);
int fd = open("/tmp/myfifo", O_WRONLY);
char *foo, *bar;
...
write(fd, foo, strlen(foo)*sizeof(char));
write(fd, bar, strlen(bar)*sizeof(char));
reader.c
int fd = open("/tmp/myfifo", O_RDONLY);
char buf[100];
read(fd, buf, ??);
My question is:
Since it's not know before hand how many bytes will foo and bar have, how can I know how many bytes to read from reader.c?
Because if I, for example, read 10 bytes in reader and foo and bar are together less than 10 bytes, I will have them both in the same variable and that I do not want.
Ideally I would have one read function for every variable, but again I don't know before hand how many bytes will the data have.
I thought about adding another write instruction in writer.c between the write for foo and bar with a separator and then I would have no problem decoding it from reader.c. Is this the way to go about it?
Thanks.
You'll have to define some kind wire protocol or serialization/deserialization format so that your reader knows how to interpret the data that it's reading from the fifo. Using a separator is the simplest way to go about this but you'll run into problems if your separator ever appears as part of the data output of your writer.
Slightly farther along the complexity scale, your protocol might define both a separator and a way of indicating the length of each "piece" or "message" of data that you're sending.
Finally, this problem is more thoroughly solved by writing serialized messages which your writer will then deserialize after receiving. You may be interested in using something like Protocol Buffers or Thrift to achieve this (with the added bonus that you can implement your reader or writer in a number of different programming languages without modifying your protocol).
A separator is one way to go about it, and this will work fine, as long as you know the order of your data, and you use the separator as only a separator, and never as part of your data.
Another way is to precede each write to the pipe with the number of bytes to follow, in a fixed width. Thus, you will know how much data is about to come down the pipe. Use a fixed width, so you know exactly how long the width field will be, so you know both when to start and stop reading each chunk of data.
To generalize WhirlWind's answer slightly, you've got to establish a protocol of SOME variety. There has to be order to what you are sending or else you don't know top from bottom, as you point out.
Both of WhirlWind's suggestions will work. You can also go so far as to implement a custom (or standard) protocol on top of a pipe or FIFO to make porting your code to a more distributed environment with disparate systems and easier task later. The crux of the issue, though, is that you've got to set RULES for communicating before you're able to actually communicate.
Many of the other answers mention using some sort of protocol for your data and I believe this is the correct approach. This protocol can be as simple or complex as necessary. I have provided a couple of examples that you may find useful1.
In a simple case, you may only have a length byte followed by the data byte(s) (i.e. C string).
Writer:
Reader:
In a more complex case, you may have a length byte followed by data bytes containing more than a simple C string.
Common Header:
Writer:
Reader:
1 - This code was written from memory and is untested.
A separator is indeed one way to do this - and conveniently enough, C strings come with such a separator - the nul-terminator at the end of the string.
If you change your
write()
calls so that they also write out the nul-terminator (note thatsizeof(char)
is defined to be 1, so it can be left out):You can then pick apart the strings after you read them in (you will still need to read them into one buffer and then break them apart, unless you read them a character at a time).