2010-05-20 233 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