пример потока именованных каналов, не показывающий результат - PullRequest
0 голосов
/ 01 сентября 2018

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

using (NamedPipeServerStream namedPipeServer = new NamedPipeServerStream("test-pipe", PipeDirection.InOut, 1, PipeTransmissionMode.Message))
{
    byte[] bytes = Encoding.Default.GetBytes("Hello, it's me!\n");
    namedPipeServer.WaitForConnection();
    Console.WriteLine("A client has connected!");
    namedPipeServer.Write(bytes, 0, bytes.Length);
}

Вот второй проект:

using (NamedPipeClientStream namedPipeClient = new NamedPipeClientStream(".", "test-pipe", PipeDirection.InOut))
{
    namedPipeClient.Connect();
    namedPipeClient.ReadMode = PipeTransmissionMode.Message;
    string serverResponse = string.Empty;
    byte[] readBytes = new byte[5];
    while (!namedPipeClient.IsMessageComplete)
    {
        namedPipeClient.Read(readBytes, 0, readBytes.Length);
        serverResponse = Encoding.Default.GetString(readBytes);
        readBytes = new byte[5];
    }
    System.Console.WriteLine(serverResponse);
    byte[] writeBytes = Encoding.Default.GetBytes("Hello from client!\n");
    namedPipeClient.Write(writeBytes, 0, writeBytes.Length);
}

Что с этим не так?

Спасибо

Ответы [ 2 ]

0 голосов
/ 01 сентября 2018

Клиент не получает никакого сообщения от сервера, потому что namedPipeClient.IsMessageComplete должен вызываться после операции чтения. См. PipeStream.IsMessageComplete в документах:

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

В противном случае namedPipeClient.IsMessageComplete возвращает true, а код внутри while -loop не выполняется. Таким образом, вам нужно переписать цикл while в цикл do-while, чтобы убедиться, что операция чтения происходит до тестирования namedPipeClient.IsMessageComplete.

Но есть и другие проблемы, см. Комментарии для объяснений:

using (NamedPipeClientStream namedPipeClient = new NamedPipeClientStream(".", "test-pipe", PipeDirection.InOut))
{
    namedPipeClient.Connect();
    namedPipeClient.ReadMode = PipeTransmissionMode.Message;

    // StringBuilder is more efficient for string concatenation
    StringBuilder serverResponse = new StringBuilder();

    byte[] readBytes = new byte[5];

    do
    {
        // You need to store number of bytes read from pipe (to readCount variable).
        // It can be less then the length of readBytes buffer, in which case
        // GetString() would decode characters beyond end of message.
        var readCount = namedPipeClient.Read(readBytes, 0, readBytes.Length);
        var readText = Encoding.Default.GetString(readBytes, 0, readCount);

        // You original code "overwrites" content of serverResponse variable instead
        // of concatenating it to the previous value. So you would receive only 
        // the last part of the server message.
        serverResponse.Append(readText);

        // It is not needed to create new buffer, you can just reuse existing buffer
        //readBytes = new byte[5];

    // IsMessageComplete is now tested after read operation
    } while (!namedPipeClient.IsMessageComplete);

    System.Console.WriteLine(serverResponse.ToString());

    // You current server implementation exits as soon as it sends message to the client
    // and does not wait for incomming message. You'll have to change server accordingly 
    // to be able to send a message back to the server.
    //byte[] writeBytes = Encoding.Default.GetBytes("Hello from client!\n");
    //namedPipeClient.Write(writeBytes, 0, writeBytes.Length);
}
<Ч />

EDIT:

Когда именованный канал находится в режиме PipeTransmissionMode.Message, каждый вызов NamedPipeServerStream.Write() на сервере отправляет данные через канал в виде отдельного сообщения. Затем клиент может получать эти сообщения отдельно друг от друга (в отличие от режима PipeTransmissionMode.Byte, где клиент получает только один непрерывный поток байтов, независимо от того, сколько операций записи выполнял сервер, используя NamedPipeServerStream.Write()).

Когда клиент читает данные из канала (namedPipeClient.Read()), метод может вернуть меньше данных, чем запрошено (например, когда в буфере приема недостаточно места для хранения всего сообщения, или сообщение короче, чем запрошенный количество байтов), см. документы .

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

Затем вы можете использовать namedPipeClient.IsMessageComplete и readCount, чтобы обнаружить это. Позвольте мне объяснить это на одном примере: представьте, что сервер отправляет клиенту сообщение ABCDEFGHIJKL, закодированное в байтовый массив как { 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76 }. Это сообщение имеет длину 12 байт, поэтому оно не помещается в ваш принимающий буфер (readBytes) длиной 5 байт. Поэтому, когда клиент впервые читает из канала, используя namedPipeClient.Read(), приемный буфер будет содержать только первые 5 байтов ({ 65, 66, 67, 68, 69 }, соответствующих ABCDE) сообщения. И здесь namedPipeClient.IsMessageComplete поможет нам, потому что он вернет false, указывая, что мы не получили полное сообщение, осталось еще несколько байтов, и мы должны продолжить чтение.

Второе чтение из канала будет аналогичным, мы прочтем вторую часть сообщения ({ 70, 71, 72, 73, 74 } соответствует FGHIJ), namedPipeClient.IsMessageComplete по-прежнему false, что указывает на неполное сообщение.

Когда третье чтение из канала завершится, будут прочитаны только 2 оставшихся байта ({ 75, 76 }, соответствующих KL), но наш буфер по-прежнему имеет длину 5 байтов, поэтому он будет выглядеть следующим образом: ({ 75, 76, 72, 73, 74 } соответствует до KLHIJ). Значения 72, 73, 74 все еще присутствуют в предыдущей итерации цикла. И теперь важно сохранить значение, возвращаемое из namedPipeClient.Read() в переменную readCount. Он будет содержать значение 2, указывающее, что допустимы только 2 байта буфера bytesRead, а оставшиеся байты следует игнорировать.

0 голосов
/ 01 сентября 2018

Попробуйте пользователю StreamReader для чтения сообщений из обоих каналов. Сервер:

using (NamedPipeServerStream namedPipeServer = new NamedPipeServerStream("test-pipe", PipeDirection.InOut, 1, PipeTransmissionMode.Byte))
{
    namedPipeServer.WaitForConnection();
    Console.WriteLine("A client has connected!");

    byte[] bytes = Encoding.Default.GetBytes("Hello, it's me!\n");
    namedPipeServer.Write(bytes, 0, bytes.Length);
    namedPipeServer.WaitForPipeDrain();

    var reader = new StreamReader(namedPipeServer);
    var msg = reader.ReadLine();
    Console.WriteLine(msg);
}

Клиент:

using (NamedPipeClientStream namedPipeClient = new NamedPipeClientStream(".", "test-pipe", PipeDirection.InOut))
{
    namedPipeClient.Connect();

    var reader = new StreamReader(namedPipeClient);
    var msg = reader.ReadLine();
    Console.WriteLine(msg);

    byte[] writeBytes = Encoding.Default.GetBytes("Hello from client!\n");
    namedPipeClient.Write(writeBytes, 0, writeBytes.Length);
    namedPipeClient.WaitForPipeDrain();
}
...