Неблокирующее чтение и блокирующая запись в Posix PIPE

Я пишу клиент-серверное приложение, используя posix FIFO для связи. И клиент, и сервер представляют собой однопоточное и однопроцессное приложение.

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

Идея довольно проста: сервер просматривает все каналы от клиента к серверу и проверяет, есть ли там что-нибудь для чтения.

Моя первая реализация была примерно такой:

/* SERVER */
int desc = open(pipeName, O_RDONLY | O_NDELAY); //1
assert(desc > 0); //just simplyfing error handling
int read = read(desc, buffer, BUFSIZE); //2
if(read > 0)
  do_stuff();
close(desc); //3

/* CLIENT */
int desc = open(pipeName, O_WRONLY) //4
assert(desc > 0); //just simplyfing error handling
int read = write(desc, buffer, BUFSIZE) //5
close(desc); //6

Насколько мне известно, этот код недействителен.

Есть условия гонки и, например, такой порядок вызовов, как: 1, 2, 4, 3, 5, 6 - вероятно, вызовет SIGPIPE.

Проблема в том, что открытие неблокирующего чтения всегда успешно, даже если на другой стороне PIPE нет писателя. Это означает, что если клиент заблокируется на open (), тогда сервер сделает неблокирующее открытие (что разблокирует клиента), а затем read (), который вернет 0, потому что в данный момент ничего не будет в PIPE и close () после этого, тогда, когда управление будет вернитесь к клиенту, который хочет выполнить write () на открытом PIPE, это вызовет SIGPIPE, поскольку считыватель больше не доступен (сервер уже закрыл канал).

На данный момент я вижу два решения:

  1. Обработайте SIGPIPE в клиенте с помощью "tryAgain ()" - это выглядит очень плохо, если я сделаю это, нет никакой гарантии, что он будет работать в любой момент - это будет зависеть от возможности хорошего порядка обработки команд ...
  2. Продолжайте читать PIPE open все время на сервере (откройте его один раз и закройте, когда соединение будет считаться завершенным) - это неудобно для моей архитектуры приложения, но, конечно, возможно. Думаю, это решит проблему, но я не уверен в этом.

Вот мои вопросы:

  1. Правильный ли какой-либо из этих двух подходов?
  2. Есть ли другой способ справиться с этим сценарием?
  3. Как вы думаете, второе решение будет работать правильно?
  4. Что бы вы выбрали и почему?

Спасибо за каждый ответ.


person Yester    schedule 26.05.2013    source источник
comment
ОТ: Это должно быть assert(desc != -1); или assert(desc >= 0);, поскольку open() возвращает -1 в случае ошибки или неотрицательное целое число в случае успеха.   -  person alk    schedule 26.05.2013
comment
не редактируйте вопрос / код плаката. Либо прокомментируйте, либо ответьте, как это неправильно.   -  person xaxxon    schedule 26.05.2013
comment
Когда вы говорите, что он должен обслуживать несколько клиентов, вы имеете в виду одновременно? Потому что ваш код обрабатывает только по одному. Никакие изменения, которые обрабатывают sigpipe или read pipe, все время открыты на сервере, не заставят его обрабатывать несколько соединений одновременно.   -  person xaxxon    schedule 26.05.2013
comment
Ну, не одновременно, но я хочу, чтобы это произвело такое впечатление. Я имею в виду - он может перебирать всех клиентов и всегда обрабатывать одного, готового как можно скорее.   -  person Yester    schedule 26.05.2013
comment
И извините за это редактирование. Ты прав.   -  person Yester    schedule 26.05.2013
comment
ха-ха, на самом деле я не понимал, что это ВАШЕ редактирование. Это нормально, и если вы хотите это изменить, ничего страшного. Я думал, что это сделал кто-то другой, но не обращал внимания.   -  person xaxxon    schedule 26.05.2013
comment
Привет, @xaxxon, журнал редактирования ясно показывает, как было редактирование, не так ли? ;-)   -  person alk    schedule 26.05.2013
comment
Да. Следовательно, не обращая внимания   -  person xaxxon    schedule 26.05.2013


Ответы (1)


Мне сложно понять ваш вопрос. Вещи

Клиент будет блокировать при open () ‹== ничего не блокирует при открытии

Используйте select или epoll, чтобы определить, какие файловые дескрипторы доступны для чтения и записи.

Тогда просто позвоните, прочитайте об этом.

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

http://linux.die.net/man/4/epoll

https://banu.com/blog/2/how-to-use-epoll-a-complete-example-in-c/

person xaxxon    schedule 26.05.2013
comment
Сервер может опрашивать, чтобы узнать, кто из клиентов хочет что-то написать, и, вероятно, это будет работать (я имею в виду, что он обнаружит, что некоторые из каналов открываются клиентами для записи и ждут, когда читатель начнет писать в этот пакет). Но я не уверен, что это изменит проблему. Тем не менее, если сервер обнаружит, что есть клиент, который хочет что-то записать, и он откроется, а затем будет читать с флагом NODELAY, это приведет к тому, что он прочитает 0 байтов, а затем закроется (сервер не может ждать данных, поскольку это только один поток!). - person Yester; 26.05.2013
comment
Это означает, что у клиента будет проблема с sam - его инструкция открытия будет разблокирована, но когда процесс достигнет записи, на другой стороне (уже закрытой) не будет считывателя, и это вызовет SIGPIPE. Или я не прав? - person Yester; 26.05.2013
comment
вы ошибаетесь, потому что, если вы правильно опрашиваете, вы будете уведомлены о том, что другая сторона прервала соединение, поэтому вы можете знать, что не следует вызывать запись на нем. - person xaxxon; 26.05.2013
comment
Хм, тогда попробую сегодня так реализовать. Я полагаю, что в моем OO-коде на C ++ может быть какая-то проблема с обработкой системных уведомлений, но я думаю, что с этим нет ничего невозможного - это будет просто ужасно выглядеть;) Спасибо за этот ответ. - person Yester; 26.05.2013