Канал StdIn не очищается, когда входные данные не содержат достаточной ошибки?

#c# #linux #.net-core

#c# #linux #.net-core

Вопрос:

Я работаю над программой, которая запускает процесс, затем записывает в stdIn процесса и считывает из stdOut того же процесса.

Моя проблема в том, что если у меня не большой набор данных для записи в stdIn, например 1 байт, я не могу получить данные из стандартного вывода, потому что ReadAsync функция стандартного вывода никогда не возвращается.

ОС: Ubuntu 18.04 Платформа: dotnet core 2.2 Язык: C#

Вот код, который я использую для записи в stdIn:

     public static async Task WriteToStdInAsync(
        Stream input,
        Stream stdIn
        )
    {
        await Task.Yield();
        byte[] buffer = new byte[1024 * 64];
        int read;
        while ((read = await input.ReadAsync(buffer, 0, buffer.Length)) > 0)
        {
            await stdIn.WriteAsync(buffer, 0, read);
        }
        await stdIn.FlushAsync();
    }
  

Я считываю из потока «input», размер которого равен 1 байту, а затем передаю данные в поток stdIn.

Тогда я пытаюсь вернуть этот байт таким образом:

     public static async Task ReadFromStdOutAsync(
        Stream output,
        Stream stdOut
        )
    {
        await Task.Yield();
        byte[] buffer = new byte[1024 * 64];
        int read;
        while ((read = await stdOut.ReadAsync(buffer, 0, buffer.Length)) != 0)
        {
            await output.WriteAsync(buffer, 0, read);
        }
    }
  

await stdOut.ReadAsync(buffer, 0, buffer.Length) — в этом случае никогда не возвращается.

Если я увеличу размер входного потока до 100 КБ (меньшие значения не работают), он работает нормально. Также это работает для небольшого размера, если я закрываю поток stdIn после отправки всех данных в stdIn. Как вы можете видеть, я пытался сбросить stdIn для принудительной передачи данных, но это не работает. Я предполагаю, что в ОС существует дополнительный уровень буферизации, который не позволяет stdOut просматривать данные.

Вот как я создаю процесс:

         return new Process() {
            EnableRaisingEvents = false,                    
            StartInfo = new ProcessStartInfo {
                FileName = "python3",
                Arguments = $"{Settings.PipesEchoServer}",                    
                RedirectStandardOutput = true,
                RedirectStandardInput = true,
                UseShellExecute = false
            },
        };
  

Я попытался проверить исходный код FileStream, Stream, Process, но не смог найти ответ.

https://github.com/dotnet/corefx/blob/master/src/Common/src/CoreLib/System/IO/Stream.cs
https://github.com/dotnet/corefx/blob/master/src/Common/src/CoreLib/System/IO/FileStream.cs
https://github.com/dotnet/corefx/blob/master/src/Common/src/CoreLib/System/IO/FileStream.Unix.cs
https://github.com/dotnet/corefx/blob/master/src/System.Диагностика.Process/src/System/Diagnostics/Process.Unix.cs

Буду признателен за любую помощь.

Отредактировано:

Я получаю потоки таким образом:

         p.StandardInput.BaseStream,
        p.StandardOutput.BaseStream, 
  

где p находится экземпляр Process класса

Отредактировано 2: Я думаю, что нашел проблему. Я использую скрипт Python:

 import sys

read = sys.stdin.buffer.read(1024)
while len(read) != 0:
    sys.stdout.buffer.write(read)
    sys.stdout.buffer.flush()
    read = sys.stdin.buffer.read(1024 * 100)
  

но, похоже, sys.stdin.buffer.read не возвращает результаты, пока все 1024 байта из stdIn не станут красными. Я изменил его на sys.stdin.buffer.read(1) , и теперь это работает.

Кстати, не уверен, что это лучший способ работы с каналами в python, но это самый простой способ, который я нашел для передачи двоичных данных.

Спасибо Готцу за идею, о которой стоит прочитать setvbuf

Комментарии:

1. в c есть функция для настройки типа буферизации ввода-вывода setvbuf(stdout, NULL, _IOLBF, 0); , возможно, вы захотите проверить, можно ли использовать это в c #. строка устанавливает стандартный вывод в буферизацию строк. может быть, присмотреться к имеющимся у вас вариантам с setvbuf

2. Я вызываю скрипт на python, который использует sys.stdout.buffer для перенаправления данных в стандартный вывод. Я использую это, потому что я не нашел лучшего способа передачи двоичных данных из stdin в stdout. Я изменил способ чтения из буфера с sys.stdin.buffer.read(1024* 100) на sys.stdin.buffer.read(1) , и это помогло. Похоже, sys.stdin.buffer.read не возвращает значение, пока не будет получено все количество байтов

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

4. наличие заголовка — хорошая идея, я буду иметь это в виду, когда перейду от этого POC к реальной программе. большое спасибо 🙂