Тайм-аут чтения сервера именованных каналов - PullRequest
8 голосов
/ 15 июля 2011

При использовании C # NamedPipeServerStream, в случае, если клиент не отправляет какой-либо шаблон конца сообщения (например, \ r \ n, когда сервер читает с помощью ReadLine ()), методы NamedPipeServerStream Read будут ждать вечно, и никакие Abort () или Interupt ( ) методы будут работать в этом потоке.

С:
1) Stream.ReadTimeout не поддерживается для NamedPipeServerStream
2) Abort () или Interupt () не работают в потоке
3) NamedPipeServerStream.Disconnect () Пустая работа
Непонятно, как настроить таймаут на операции чтения NamedPipeServerStream?


Позвольте мне привести пример. Спецификация IPC у нас требует обмена \ 0-завершенными строками. Клиент отправляет сообщение, сервер обрабатывает сообщение и по мере необходимости отправляет ответ. Если клиент не отправляет \ 0 в конце (клиент не наш, поэтому мы не можем гарантировать правильность его работы), метод Read будет ждать вечно, а клиент (так как мы его не контролируем) может ждать вечно ответ тоже.

Далее приведен упрощенный пример реализации:

    public void RestartServer()
    {
        _pipeServerThread.Interrupt();  //doesn't affect Read wait
        _pipeServerThread.Abort();      //doesn't affect Read wait
    }

    private void PipeServerRun(object o) //runs on _pipeServerThread
    {
        _pipeServer = new NamedPipeServerStream(_pipeName, InOut, 100,
                      PipeTransmissionMode.Message, PipeOptions.WriteThrough);
        //_pipeServer.ReadTimeout = 100; //System.InvalidOperationException: Timeouts are not supporte d on this stream.

        // Wait for a client to connect
        while (true)
        {
            _pipeServer.WaitForConnection();
            string request = ReadPipeString();
            //... process request, send response and disconnect
        }
    }

    /// <summary>
    /// Read a \0 terminated string from the pipe
    /// </summary>
    private string ReadPipeString()
    {
        StringBuilder builder = new StringBuilder();
        var streamReader = new StreamReader(_pipeServer);

        while (true)
        {
            //read next byte 
            char[] chars = new char[1];
            streamReader.Read(chars, 0, 1); // <- This will wait forever if no \0 and no more data from client

            if (chars[0] == '\0') return builder.ToString();
            builder.Append(chars[0]);
        }
    }

Так как установить время ожидания для операций чтения NamedPipeServerStream?

Ответы [ 2 ]

3 голосов
/ 20 июля 2011

Поскольку вы запускаете канал в режиме сообщений, вы должны сначала прочитать все сообщение в буфер byte[] или поток памяти, а , а затем решить, является ли оно действительным, и декодировать его. Канальные сообщения имеют определенную длину. Его нельзя получить явно, но он появляется, когда вы читаете из канала в режиме сообщений. Win32 ReadFile завершается с ошибкой ERROR_MORE_DATA, если в сообщении все еще есть непрочитанные байты, затем возвращается TRUE, чтобы указать, что сообщение окончено. После этого вызов на ReadFile будет блокироваться, пока не будет доступно новое сообщение. StreamReader естественно не знает ничего из этого и блокирует вашу ветку.

Обновление: для реализации тайм-аутов используйте асинхронный ввод-вывод (Stream.BeginRead). StreamReader не поддерживает это напрямую. Если вам абсолютно необходимо его использовать, напишите поток-оболочку, который будет реализовывать Read в терминах BeginRead в базовом потоке и поддерживать таймауты, отмену и т. Д.

1 голос
/ 20 июля 2011

Попробуйте установить NamedPipeServerStream.ReadMode и / или .TransmissionMode в байт.Независимо от этого вы должны использовать доступные методы BeginRead / EndRead с NamedPipeServerStream.Таким образом, вы можете самостоятельно реализовать логику тайм-аута.

...