FIFO 实施

发布于 2024-09-02 04:42:38 字数 659 浏览 11 评论 0原文

考虑以下代码:

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, ??);

我的问题是:

由于事先不知道 foo 和 bar 将有多少字节,我如何知道要从 reader.c 读取多少字节?
因为,例如,如果我在 reader 中读取 10 个字节,而 foo 和 bar 总共不到 10 个字节,那么我会将它们放在同一个变量中,但我不希望这样做。
理想情况下,我将为每个变量都有一个读取函数,但我事先也不知道数据有多少字节。
我考虑在 writer.c 中在 foo 和 bar 的 write 之间添加另一个写入指令,并使用分隔符,然后从 reader.c 解码它就没有问题了。这是解决问题的方法吗?

谢谢。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

柳絮泡泡 2024-09-09 04:42:38

许多其他答案都提到对数据使用某种协议,我相信这是正确的方法。该协议可以根据需要简单或复杂。我提供了几个您可能会觉得有用的示例1


在简单的情况下,您可能只有一个长度字节,后跟数据字节(即 C 字符串)。

+--------------+
| length byte  |
+--------------+
| data byte(s) |
+--------------+

写入器:

uint8_t foo[UCHAR_MAX+1];
uint8_t len;
int fd;

mkfifo("/tmp/myfifo", 0660);
fd = open("/tmp/myfifo", O_WRONLY);

memset(foo, UCHAR_MAX+1, 0);
len = (uint8_t)snprintf((char *)foo, UCHAR_MAX, "Hello World!");

/* The length byte is written first followed by the data. */
write(fd, len, 1);
write(fd, foo, strlen(foo));

读取器:

uint8_t buf[UCHAR_MAX+1];
uint8_t len;
int fd;

fd = open("/tmp/myfifo", O_RDONLY);

memset(buf, UCHAR_MAX+1, 0);

/* The length byte is read first followed by a read 
 * for the specified number of data bytes.
 */
read(fd, len, 1);
read(fd, buf, len);

在更复杂的情况下,您可能有一个长度字节,后跟包含多个简单 C 字符串的数据字节。

+----------------+
|  length byte   |
+----------------+
| data type byte |
+----------------+
|  data byte(s)  |
+----------------+

通用标头:

#define FOO_TYPE 100
#define BAR_TYPE 200

typedef struct {
    uint8_t type;
    uint32_t flags;
    int8_t msg[20];
} __attribute__((aligned, packed)) foo_t;

typedef struct {
    uint8_t type;
    uint16_t flags;
    int32_t value;
} __attribute__((aligned, packed)) bar_t;

编写者:

foo_t foo;
unsigned char len;
int fd;

mkfifo("/tmp/myfifo", 0660);
fd = open("/tmp/myfifo", O_WRONLY);

memset(&foo, sizeof(foo), 0);
foo.type = FOO_TYPE;
foo.flags = 0xDEADBEEF;
snprintf(foo.msg, 20-1, "Hello World!");

/* The length byte is written first followed by the data. */
len = sizeof(foo);
write(fd, len, 1);
write(fd, foo, sizeof(foo));

读者:

uint8_t buf[UCHAR_MAX+1];
uint8_t len;
uint16_t type;
union data {
    foo_t * foo;
    bar_t * bar;
}
int fd;

fd = open("/tmp/myfifo", O_RDONLY);

memset(buf, UCHAR_MAX+1, 0);

/* The length byte is read first followed by a read 
 * for the specified number of data bytes.
 */
read(fd, len, 1);
read(fd, buf, len);

/* Retrieve the message type from the beginning of the buffer. */
memcpy(&type, buf, sizeof(type));

/* Process the data depending on the type. */
switch(type) {
    case FOO_TYPE:
        data.foo = (foo_t)buf;
        printf("0x%08X: %s\n", data.foo.flags, data.foo.msg); 
        break;
    case BAR_TYPE:
        data.bar = (bar_t)buf;
        printf("0x%04X: %d\n", data.bar.flags, data.bar.value); 
        break;
    default:
        printf("unrecognized type\n");
}

1 - 此代码是凭记忆编写的,未经测试。

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).

+--------------+
| length byte  |
+--------------+
| data byte(s) |
+--------------+

Writer:

uint8_t foo[UCHAR_MAX+1];
uint8_t len;
int fd;

mkfifo("/tmp/myfifo", 0660);
fd = open("/tmp/myfifo", O_WRONLY);

memset(foo, UCHAR_MAX+1, 0);
len = (uint8_t)snprintf((char *)foo, UCHAR_MAX, "Hello World!");

/* The length byte is written first followed by the data. */
write(fd, len, 1);
write(fd, foo, strlen(foo));

Reader:

uint8_t buf[UCHAR_MAX+1];
uint8_t len;
int fd;

fd = open("/tmp/myfifo", O_RDONLY);

memset(buf, UCHAR_MAX+1, 0);

/* The length byte is read first followed by a read 
 * for the specified number of data bytes.
 */
read(fd, len, 1);
read(fd, buf, len);

In a more complex case, you may have a length byte followed by data bytes containing more than a simple C string.

+----------------+
|  length byte   |
+----------------+
| data type byte |
+----------------+
|  data byte(s)  |
+----------------+

Common Header:

#define FOO_TYPE 100
#define BAR_TYPE 200

typedef struct {
    uint8_t type;
    uint32_t flags;
    int8_t msg[20];
} __attribute__((aligned, packed)) foo_t;

typedef struct {
    uint8_t type;
    uint16_t flags;
    int32_t value;
} __attribute__((aligned, packed)) bar_t;

Writer:

foo_t foo;
unsigned char len;
int fd;

mkfifo("/tmp/myfifo", 0660);
fd = open("/tmp/myfifo", O_WRONLY);

memset(&foo, sizeof(foo), 0);
foo.type = FOO_TYPE;
foo.flags = 0xDEADBEEF;
snprintf(foo.msg, 20-1, "Hello World!");

/* The length byte is written first followed by the data. */
len = sizeof(foo);
write(fd, len, 1);
write(fd, foo, sizeof(foo));

Reader:

uint8_t buf[UCHAR_MAX+1];
uint8_t len;
uint16_t type;
union data {
    foo_t * foo;
    bar_t * bar;
}
int fd;

fd = open("/tmp/myfifo", O_RDONLY);

memset(buf, UCHAR_MAX+1, 0);

/* The length byte is read first followed by a read 
 * for the specified number of data bytes.
 */
read(fd, len, 1);
read(fd, buf, len);

/* Retrieve the message type from the beginning of the buffer. */
memcpy(&type, buf, sizeof(type));

/* Process the data depending on the type. */
switch(type) {
    case FOO_TYPE:
        data.foo = (foo_t)buf;
        printf("0x%08X: %s\n", data.foo.flags, data.foo.msg); 
        break;
    case BAR_TYPE:
        data.bar = (bar_t)buf;
        printf("0x%04X: %d\n", data.bar.flags, data.bar.value); 
        break;
    default:
        printf("unrecognized type\n");
}

1 - This code was written from memory and is untested.

情仇皆在手 2024-09-09 04:42:38

分隔符是解决此问题的一种方法,只要您知道数据的顺序,并且仅将分隔符用作分隔符,而不将其用作数据的一部分,那么这种方法就可以很好地工作。

另一种方法是在每次写入管道之前以固定宽度指定要跟随的字节数。因此,您将知道有多少数据将通过管道传输。使用固定宽度,这样您就可以准确地知道宽度字段的长度,从而知道何时开始和停止读取每个数据块。

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.

暖心男生 2024-09-09 04:42:38

分隔符确实是实现此目的的一种方法 - 而且很方便,C 字符串带有这样的分隔符 - 字符串末尾的 null 终止符。

如果您更改 write() 调用,以便它们也写出 nul 终止符(请注意,sizeof(char) 被定义为 1,因此可以保留out):

write(fd, foo, strlen(foo) + 1);
write(fd, bar, strlen(bar) + 1);

然后,您可以在读入字符串后将它们分开(您仍然需要将它们读入一个缓冲区,然后将它们分开,除非您一次读取它们一个字符)。

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 that sizeof(char) is defined to be 1, so it can be left out):

write(fd, foo, strlen(foo) + 1);
write(fd, bar, strlen(bar) + 1);

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).

水中月 2024-09-09 04:42:38

为了稍微概括一下 WhirlWind 的答案,您必须建立某种协议。正如您所指出的,您发送的内容必须有顺序,否则您不知道从上到下。

WhirlWind 的两个建议都有效。您还可以在管道或 FIFO 之上实现自定义(或标准)协议,以便将代码移植到具有不同系统的更加分布式的环境中,并且以后的任务更容易。然而,问题的关键在于,在真正进行沟通之前,您必须先设定沟通规则。

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.

疾风者 2024-09-09 04:42:38

您必须定义某种有线协议或序列化/反序列化格式,以便您的阅读器知道如何解释它从 fifo 读取的数据。使用分隔符是解决此问题的最简单方法,但如果分隔符作为编写器的数据输出的一部分出现,则会遇到问题。

在复杂性范围上稍微远一些,您的协议可能会定义分隔符和指示您发送的数据的每个“片段”或“消息”的长度的方法。

最后,通过编写序列化消息可以更彻底地解决这个问题,您的编写者将在收到消息后对其进行反序列化。您可能有兴趣使用 协议缓冲区Thrift 来实现这一目标(还有额外的好处,您可以用多种不同的编程语言实现您的阅读器或编写器,而无需修改您的协议) 。

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).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文