Пакеты по именованной трубе? Однобайтовый буфер или предварительный размер? - PullRequest
1 голос
/ 16 января 2010

Я хочу отправлять «пакеты» данных (то есть дискретные сообщения) между двумя программами через именованные каналы. Учитывая, что я должен предоставить буфер и размер буфера для read, и учитывая, что команда чтения блокирует (я полагаю), мне нужно либо иметь размер буфера, который гарантирует, что я никогда не получу недостаточную производительность, либо знать размер сообщения заранее. Я не хочу, чтобы программа-отправитель знала размер буфера и дополняла его.

На мой взгляд, есть три способа сделать это.

  1. Предварительно добавьте в каждый пакет размер отправляемого сообщения, чтобы прослушивающая программа могла прочитать столько байтов.
  2. Считывание из канала байта за раз и прослушивание специального значения конца потока.
  3. Лучший способ

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

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

Какой лучший путь?

Ответы [ 2 ]

3 голосов
/ 16 января 2010

С именованными каналами чтение и запись являются (или могут быть) атомарными. В определенных пределах, если вы записываете, скажем, 1024 байта в канал, вызов чтения на другом конце, который ищет как минимум 1024 байта, фактически получит 1024 байта, даже если во время передачи в канале было больше данных. читать. Кроме того, и всегда, если в именованном канале всего 1024 байта, а чтение требует 4096 байтов, он получит 1024 байта с первой попытки и заблокирует только при следующей попытке.

Вы говорите:

Учитывая, что я должен предоставить буфер и размер буфера для чтения,

Вы делаете ...

и учитывая, что команда чтения блокирует (я полагаю),

Да, если вы не установите O_NONBLOCK в дескрипторе файла ...

У меня либо должен быть размер буфера, который гарантирует, что я никогда не получу недостаточную производительность,

Какие сообщения вы отправляете? С каким размером ты имеешь дело? Килобайт, мегабайт, больше?

или узнать размер сообщения заранее.

Нет особой проблемы с наличием, скажем, буфера 4 КБ в считывателе и чтением сообщения кусками. Проблема в том, что вы знаете, когда вы дойдете до конца сообщения. Большинству протоколов требуется длина заранее, потому что это позволяет легко писать код считывателя.

Если вы собираетесь сделать маркер «конца потока» (EOS), вы делаете «внутриполосную сигнализацию». И это вызывает проблемы. Какой персонаж вы собираетесь использовать? Что происходит, когда этот символ появляется в данных? Вам нужен механизм выхода, такой как символ, который означает «следующий символ не является маркером EOS». Например, в тексте, связанном с программированием, для этого используется обратный слеш. В терминале control-V часто служит цели.

Я не хочу, чтобы программа-отправитель знала размер буфера и дополняла его.

Почему отправителю трудно узнать размер буфера? И зачем это нужно "набирать"?

Если вы имеете дело с большими объемами данных (например, от килобайта и выше), односимвольное решение вряд ли даст приемлемую производительность. Я думаю, что было бы лучше, если бы отправитель мог определить размер пакета и сообщить об этом читателю, или разработать протокол так, чтобы были ограничения на размер пакета. Если вам нужно передать произвольные объемы данных, используйте протокол, который гласит:

  • Большое количество данных неизвестного общего размера.
  • Для каждого подпакета в сообщении говорится «это подпакет размером NN КБ».
  • Для последнего подпакета размер может быть короче - это нормально и может указывать на «конец большого количества данных».
  • Если последний подпакет имеет «полный размер», вы можете отправить пустой последний пакет, чтобы указать EOS.
  • В качестве альтернативы, если подпакеты могут иметь переменный размер, вы всегда можете отправить явный пакет EOS.

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

Я думаю, что вы должны проектировать свою систему с пакетами, в которых заголовки пакетов включают размер данных (как это делают большинство сетевых протоколов, таких как TCP / IP). И если есть поток данных более высокого уровня с неизвестным размером, обработайте его в соответствии с указанными выше линиями. Но даже там лучше, если вы сможете заранее определить общий размер.

1 голос
/ 16 января 2010

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

Отправитель:

packet.ident = ftok("./mynamedpipe");
packet.pointer = shmget(packet.ident, sizeof(message), IPC_CREAT|IPC_EXCL);
strcpy(packet.pointer, message);

приемник:

message = shmat(packet.ident, NULL, NULL);   

Обратите внимание, что адрес в shmat явно не указан, чтобы предотвратить переназначение существующей памяти в процессе получателя.

...