2010-05-20 234 views
4

考虑下面的代码:的FIFO实现

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读取?
因为如果我在阅读器中读取10个字节,并且foo和bar在一起少于10个字节,我会将它们放在同一个变量中,而我不想要它们。
理想情况下,我会为每个变量都有一个读取函数,但是我再也不知道数据会有多少字节。
我想在writer.c中添加另一个写入指令,在写入foo和bar之间加上分隔符,然后我就没有问题从reader.c中解码它。这是要走的路吗?

谢谢。

+0

注意:ANSI C保证'sizeof(char)'总是等于1. – 2010-05-20 01:54:57

回答

6

分隔符是一种可行的方法,只要知道数据的顺序,并且只使用分隔符作为分隔符,并且永远不会将其作为数据的一部分,分隔符就可以正常工作。

另一种方法是在每个写入管道之前以固定宽度跟随字节数。因此,你会知道有多少数据即将下降。使用一个固定的宽度,所以你知道宽度字段的确切时间,所以你知道什么时候开始和停止读取每个数据块。

1

分隔符确实是这样做的一种方式 - 方便地说,C字符串带有这样的分隔符 - 字符串末尾的nul结束符。

如果您改变write()电话,让他​​们也写出来的NUL终止符(注意sizeof(char)被定义为1,所以它可以去掉):

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

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

1

为了概括WhirlWind的答案,你必须建立一些协议。正如你指出的那样,你必须要有秩序地发送你的内容,否则你不知道从头到尾。

WhirlWind的两个建议都可以使用。您还可以在管道或FIFO之上实现自定义(或标准)协议,以便将代码移植到具有不同系统的更分布式环境中,以后再轻松完成任务。问题的症结在于,在你能够实际沟通之前,你必须设置RULES进行通信。

1

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

沿着复杂性等级稍微更远一点,您的协议可能会同时定义一个分隔符和一种指示您发送的数据的每个“块”或“消息”长度的方式。

最后,通过编写序列化消息可以更彻底地解决此问题,然后您的作者将在接收后进行反序列化。您可能有兴趣使用诸如Protocol BuffersThrift之类的东西来实现此目的(还有额外的好处是您可以在不修改协议的情况下以多种不同的编程语言实现您的读写器)。

7

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


在一种简单的情况下,你可能只有一个长度字节之后是数据字节(一个或多个)(即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 - 此代码是从内存写入,未经测试。

+0

+1。真棒解释 – 2014-08-25 16:56:42