2010-05-20 5 views
4

Рассмотрим следующий код:FIFOs реализация

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

ответ

6

Разделитель - это один из способов обойти это, и это будет работать нормально, если вы знаете порядок ваших данных, и вы используете разделитель как разделитель и никогда не как часть ваших данных.

Другой способ заключается в том, чтобы предшествовать каждой записи в трубу с количеством последующих байтов в фиксированной ширине. Таким образом, вы узнаете, сколько данных собирается спуститься по трубе. Используйте фиксированную ширину, так что вы точно знаете, как долго будет поле ширины, поэтому вы знаете, как начать и прекратить чтение каждого фрагмента данных.

1

Разделитель действительно является одним из способов сделать это - и достаточно удобно, строки C поставляются с таким разделителем - nul-terminator в конце строки.

Если вы измените ваши write() звонки так, что они также выписывают NUL-терминатор (обратите внимание, что sizeof(char) определяется как 1, так что можно опустить):

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

Вы можете выбрать обособленно строки после того, как вы их прочтете (вам все равно нужно будет прочитать их в один буфер, а затем разбить их, если вы не прочтете их персонажем за раз).

1

Чтобы обобщить ответ WhirlWind, вы должны установить протокол из нескольких разновидностей. Должен быть порядок того, что вы отправляете, или вы не знаете сверху вниз, как вы указываете.

Оба предложения WhirlWind будут работать. Вы также можете зайти так далеко, чтобы реализовать собственный (или стандартный) протокол поверх трубы или FIFO, чтобы впоследствии портировать ваш код в более распределенную среду с разрозненными системами и упростить задачу. Суть проблемы заключается в том, что вы должны установить правила для общения, прежде чем сможете общаться.

1

Вам нужно будет определить какой-либо проводной протокол или формат сериализации/десериализации, чтобы ваш читатель знал, как интерпретировать данные, которые он читает из fifo. Использование разделителя - это самый простой способ сделать это, но вы столкнетесь с проблемами, если ваш разделитель когда-либо появится как часть вывода данных вашего автора.

Немного дальше по шкале сложности ваш протокол может определять как разделитель, так и способ указания длины каждой «части» или «сообщения» передаваемых данных.

И, наконец, эта проблема более тщательно решена путем написания сериализованных сообщений, которые ваш писатель затем десериализует после получения. Вам может быть интересно использовать что-то вроде Protocol Buffers или Thrift для этого (с добавленным бонусом, который вы можете реализовать своего читателя или писателя на нескольких языках программирования без изменения вашего протокола).

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. Удивительное объяснение –

Смежные вопросы