Как получить длину данных для чтения (надежно) в именованных каналах? - PullRequest
2 голосов
/ 18 января 2010

Я создал именованный канал со следующими флагами:

  • PIPE_ACCESS_DUPLEX - доступ на чтение / запись на обе стороны
  • PIPE_TYPE_MESSAGE - чтение типа сообщения
  • PIPE_WAIT - блокировка чтения \ записи

Со стороны сервера я звоню ConnectNamedPipe и жду подключения клиентов.

Со стороны клиента я звоню CallNamedPipe , чтобы подключиться к серверу и записать данные длиной N.

На стороне сервера:

  • После подключения клиента вызывается PeekNamedPipe для получения длины буфера, выделяемого для чтения буфера данных.
  • После получения точного размера буфера (N) я выделяю буфер длины N и вызываю ReadFile для чтения данных из Pipe.

Проблема:

  • Проблема в том, что - на однопроцессорных компьютерах API PeekNamedPipe возвращает длину буфера как 0. Из-за этого позже ReadFile завершается ошибкой.
  • после некоторого исследования я обнаружил, что из-за некоторого состояния гонки PeekNamedPipe API вызывается даже до того, как клиент помещает данные в Pipe.
  • Есть идеи, как решить это условие гонки? Мне нужно вызвать PeekNamedPipe, чтобы получить размер буфера, и PeekNamedPipe нельзя вызвать до того, как станут доступны данные.

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

Есть ли лучший и надежный способ получить длину данных, которые будут считываться из канала?

Ответы [ 3 ]

6 голосов
/ 18 января 2010

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

  • ConnectNamedPipe () на стороне сервера может возвращать ERROR_PIPE_CONNECTED, если клиенту удалось подключиться сразу после вызова CreateNamedPipe (). Просто относитесь к нему как к подключенному.
  • WaitNamedPipe на стороне клиента не устанавливает ошибку, если истекло время ожидания. Предположим, тайм-аут.
  • CreateFile () на стороне клиента может возвращать ERROR_PIPE_BUSY, если другому клиенту удалось сначала получить канал, даже после успешного вызова WaitNamedPipe (). Вернитесь в состояние WaitNamedPipe.
  • FlushFileBuffers () может возвращать ERROR_PIPE_NOT_CONNECTED, если клиент уже видел сообщение и закрыл канал. Проигнорируйте это.
  • Перекрытый вызов ReadFile () может завершиться немедленно и не вернуть ERROR_IO_PENDING. Считайте, что чтение завершено.
  • PeekNamedPipe () может вернуть 0, если сервер еще не записал в канал. Спи (1) и повторяй.
1 голос
/ 18 января 2010

Звучит так, будто вы хотите Aynschronous I / O. Просто позвольте Windows уведомить вас о доступности данных и загляните в этот момент.

0 голосов
/ 18 января 2010

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

...