Реализация 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, ??);

Мой вопрос:

Так как это не, знают перед рукой, сколько байтов нечто и панель будут иметь, как я могу знать сколько байтов читать из reader.c?
Поскольку, если я, например, читал, 10 байтов в читателе и нечто и панели - вместе меньше чем 10 байтов, у меня будут они оба в той же переменной и что я не хочу.
Идеально у меня была бы функция чтения того для каждой переменной, но снова я не знаю перед рукой, сколько байты будут данные иметь.
Я думал о добавлении другой инструкции по записи в writer.c между записью для нечто и панелью с разделителем, и затем у меня не будет проблемы при декодировании его от reader.c. Действительно ли это - способ пойти об этом?

Спасибо.

5
задан Johan 5 October 2010 в 08:53
поделиться

5 ответов

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

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

6
ответ дан 18 December 2019 в 10:42
поделиться

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

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

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

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

1
ответ дан 18 December 2019 в 10:42
поделиться

Чтобы немного обобщить ответ WhirlWind, вы должны установить протокол НЕКОТОРОГО разнообразия. Должен быть порядок в том, что вы отправляете, иначе вы не знаете сверху снизу, как вы указываете.

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

1
ответ дан 18 December 2019 в 10:42
поделиться

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

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

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

1
ответ дан 18 December 2019 в 10:42
поделиться

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


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

+--------------+
| 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);

В более сложном случае у вас может быть байт длины, за которым следуют байты данных, содержащие больше, чем простая строка 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 - Этот код был записан из памяти и не тестировался.

7
ответ дан 18 December 2019 в 10:42
поделиться
Другие вопросы по тегам:

Похожие вопросы: