Пакеты по именованному каналу? Однобайтовый буфер или предварительно заданный размер?

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

Как я вижу, есть три способа сделать это.

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

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

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

Как лучше всего пойти?


person Joe    schedule 16.01.2010    source источник


Ответы (2)


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

Ты говоришь:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

person Jonathan Leffler    schedule 16.01.2010
comment
Спасибо за это. Все вышеперечисленное - это то, что я ожидал (но лучше не предполагать, когда вы делаете что-то в первый раз). FWIW: Я ожидаю, что сообщения будут меньше 500 байт каждое. Re И зачем ему нужно «дополнять»? это было бы необходимо, если бы был фиксированный размер буфера, а длина сообщения не была кратна этому размеру (я полагаю, что читатель заблокировал бы последний частично заполненный буфер?). - person Joe; 16.01.2010
comment
Нет необходимости в прокладке; как я пытался указать, если в буфере, скажем, 500 байт, функция read() вернет эти 500 байт, даже если запросит 4096 или другой больший размер. Вы получите «краткое чтение»; это не ошибка. - person Jonathan Leffler; 17.01.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 не указывается явно, чтобы предотвратить перераспределение существующей памяти в процессе получателя.

person WarrenB    schedule 16.01.2010
comment
Спасибо, это, вероятно, более эффективный способ ведения дел. Тем не менее, я пытаюсь сделать IPC как можно более переносимым, независимым от языка и общим, поэтому я хотел бы сохранить его в ванильном потоке байтов. - person Joe; 17.01.2010