2014-01-19 1 views
1

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

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

Использование write() Я могу попробовать 0-байт написать и проверить на ошибку EPIPE, чтобы определить, есть ли подключенный клиент. Но как я могу заблокировать до тех пор, пока клиент не подключится (кроме того, что он немного спит, и попытается снова написать)?

Или есть другой, лучший способ достичь этого?

+0

Практично ли закрывать и открывать трубу? – rici

+0

Вы пытались [опрос (2)] (http://man7.org/linux/man-pages/man2/poll.2.html)? –

+0

@BasileStarynkevitch: Даже с 'poll()', канал может быть готов для записи, а затем, как раз перед тем, как вы напишете, читатель умрет: вы потеряли данные! – rodrigo

ответ

2

Оказалось, что в отличие от моего комментария к первому вопросу выше, существует прямое решение.

Данное решение предполагает, что все читатели и все авторы одного и того же FIFO совместно используют буферы ядра. Это должен быть самый логичный и простой способ реализации FIFO (учитывая их поведение), поэтому я ожидаю, что все системы, обеспечивающие FIFO, будут вести себя таким образом. Однако это просто мое предположение, а не какая-либо гарантия. Я не нашел ничего в соответствующих стандартах POSIX, чтобы поддержать или противоречить этому. Пожалуйста, сделайте трубку, если найдете другое.

Процедура тривиальна:

Когда клиент обращается в нуль неожиданно, писатель снова открывает FIFO, не закрывая исходный дескриптор первого. Это open() будет блокироваться, пока не появится новый читатель, но поскольку исходный файловый дескриптор все еще открыт, данные, уже буферизированные в FIFO, будут доступны новому читателю. Если open() преуспевает, писатель просто закрывает исходный дескриптор и вместо этого переключается на использование нового дескриптора.

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

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

Я проверил эту тривиальная стратегия работает на Linux 3.8.0-35-generic ядер на x86_64 в Ubuntu, а также 2.6.9-104.ELsmp на x86_64.


Однако я до сих пор полностью согласен с либо принять потерю данных, или изменения протокола, как это было предложено Basile Starynkevitch в комментарии к первоначальному вопросу.

Лично я нашел сокеты домена Unix (привязанные к имени пути, скажем /var/run/yourservice/unix), чтобы быть намного лучшим вариантом, поскольку он позволяет нескольким одновременным клиентам без повреждения данных (в отличие от FIFO) и гораздо более здравого протокола.

Я предпочитаю сокеты с дейтаграммой Unix с порядковым номером и длиной дейтаграммы в начале каждой дейтаграммы.(Длина помогает клиенту проверить его, прочитав всю дейтаграмму, я действительно не ожидаю, что какая-либо ОС обрезает дейтаграммы Unix.)

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

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

(В дополнение к читателю-писателю «acks» (подтверждения) я также поддерживал бы ответы «пожалуйста, повторно отправьте», если клиент использовал слишком маленький буфер для получения дейтаграммы или опустил его на пол из-за по некоторым другим причинам. А может быть, даже «гадость» для дейтаграмм клиент не знает, что делать.)

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

Вопросы?

+0

Спасибо. Я думаю, что вы правы, поэтому после некоторого рассмотрения я решил вместо этого использовать протокол на основе сокетов (который отлично работает). – Denvercoder9

+0

Вы ссылаетесь на коррупцию в фиолетовой среде с несколькими клиентами. У вас есть ссылки на дополнительную информацию? – lane

+0

@lane: Я не помню (или вижу, после повторного чтения моего ответа выше) любое соединение с * коррупцией * здесь, только для потери данных. Как я уже объяснял выше, я проверил это решение экспериментально сам (после изучения реализации ядра ядра Linux именованных каналов), поэтому я не уверен, какие ссылки на дополнительную информацию вы имеете в виду или интересуетесь. –

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

  • Нет связанных вопросов^_^