Проблема .NET NamedPipeServerStream - последовательные операции чтения возвращают одни и те же данные - PullRequest
0 голосов
/ 18 июня 2010

У меня проблема с NamedPipeServerStream - когда мой код читает данные, он просто повторяет вывод последних Read без захвата новых данных.

Вот самый маленький пример серверного кода, демонстрирующий такое поведение:

using System;
using System.IO;
using System.IO.Pipes;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static NamedPipeServerStream NPSS;

        static void Main(string[] args)
        {
            string PipeName = "Test1";

            // create asynchronous pipe server
            NPSS = new NamedPipeServerStream(PipeName, PipeDirection.InOut, -1, PipeTransmissionMode.Message, PipeOptions.Asynchronous);
            IAsyncResult resultConnect = NPSS.BeginWaitForConnection(NamedPipeConnectionCallback, null);

            Console.WriteLine("Press X to exit\n\n");
            while (Console.ReadKey(true).Key != ConsoleKey.X);

        }

        static void NamedPipeConnectionCallback(IAsyncResult resultConnection)
        {
            try
            {
                NPSS.EndWaitForConnection(resultConnection);
            }
            catch (OperationCanceledException) // this happens when calling thread (Main function) exits
            {
                return;
            }

            while (NPSS.CanRead)
            {
                // small buffer for demonstration purposes; it's much larger in the 
                //  actual code, but still exhibits same problem
                byte[] PipeDataBuffer = new byte[16];
                MemoryStream MessageStream = new MemoryStream();
                int TotalBytesRead = 0;

                do
                {
                    int BytesRead = NPSS.Read(PipeDataBuffer, 0, PipeDataBuffer.Length);
                    MessageStream.Write(PipeDataBuffer, 0, BytesRead);
                    TotalBytesRead += BytesRead;
                } while (!NPSS.IsMessageComplete);

                byte[] Message = MessageStream.ToArray();

                if (Message.Length == 0)
                    break;

                Console.WriteLine(String.Format("Message received, {0} bytes:", TotalBytesRead));
                Console.WriteLine(new ASCIIEncoding().GetString(Message));
            }

            NPSS.Disconnect();
            NPSS.BeginWaitForConnection(NamedPipeConnectionCallback, null);
        }
    }
}

Это тестовый клиент (написан на ассемблере DOS, в стиле NASM, компилируется в файл .COM).Все, что он делает, это открывает канал в виде файла (\. \ Pipe \ Test1) и записывает в него некоторые данные:

; NPTEST2.ASM
; - tests communication with named pipe

  org 0x100

  push cs
  pop ds

  mov si, pipename   ; path
  mov bx, 0x42       ; access/sharing mode
  mov cx, 0          ; attributes
  mov dx, 1          ; open file (not create/truncate)
  mov ax, 0x716c     ; long filename open
  int 0x21

  jc quit
  push ax            ; file handle returned in ax

  pop bx             ; file handle
  push bx
  mov ah, 0x40       ; write
  mov cx, (testdata_end-testdata)  ; size
  mov dx, testdata   ; ptr to data
  int 0x21

  pop bx             ; file handle
  mov ah, 0x3e       ; close
  int 0x21  

quit:
  mov ah,0x4c        ; quit
  int 0x21

pipename:
  db "\\.\pipe\Test1",0

testdata:
  db "!", 0x22, "#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
testdata_end:

И вот типичный вывод сервера, в данном случае от запускаклиент три раза подряд:

Press X to exit

Message received, 110 bytes:
!"#$%&'()*+,-./0!"#$%&'()*+,-./0123456789:;?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
Message received, 94 bytes:
!"#$%&'()*+,-./0123456789:;?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
Message received, 94 bytes:
!"#$%&'()*+,-./0123456789:;?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~

Как видите, первое сообщение на 16 байт (т. е. одна длина буфера) длиннее, чем должно быть, потому что первые 16 байт повторяются в начале.

До сих пор я пытался изменить:

  • режим передачи от сообщения к байту
  • запуск клиента и сервера на разных хостах, а не только на локальных
  • с использованием асинхронных вызовов BeginRead и EndRead вместо Read
  • преобразования кода в полностью синхронный с использованием WaitForConnection и Read

но ничего из этого не имело никакого значения для проблемы.

Может кто-нибудь пролить свет на то, что я делаю здесь неправильно, или на другие вещи, которые я могу проверить?Спасибо!

РЕДАКТИРОВАТЬ: Дальнейшее исследование - что изменило ситуацию, так это использование клиента тестирования Windows, а не клиента, работающего под NTVDM (DOS-машина).То есть этот код:

int main(int argc, char* argv[])
{
    FILE *f = fopen("\\\\.\\pipe\\Test1", "r+b");
    if (f)
    {
        fwrite("!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~", 94, 1, f);
        fclose(f);
    }
    return 0;
}

, который должен быть эквивалентным приведенному выше коду ассемблера DOS, на самом деле ведет себя правильно.

Запуск Process Monitor, когда тестовые клиенты работаютЗапуск показывает, что DOS-1 иногда имеет результат ОТМЕНА для WriteFile, а клиент Windows - нет.Небольшая проблема, так как предполагается, что это дополнение для DOS-программы: (

Ответы [ 2 ]

0 голосов
/ 19 июня 2010

Я понял это.

(это не было проблемой в NamedPipeServerStream.)

Похоже, это было из-за плохого выбора системных вызовов DOS.Если я использую открытый вызов через INT 0x21 / AH = 0x3D, он работает без проблем.Трассировка стека в Process Monitor также показывает совершенно другой путь кода для этого метода.Это все довольно странно, но, по крайней мере, это работает.Думаю, эта техническая информация никому не нужна в мире, но я все равно решил обновить:)

; NPTEST3.ASM
; - tests communication with named pipe

  org 0x100

  push cs
  pop ds

  mov dx, pipename   ; path
  mov ax, 0x3d42     ; open
  int 0x21

  jc quit
  push ax            ; file handle returned in ax

  pop bx             ; file handle
  push bx
  mov ah, 0x40       ; write
  mov cx, (testdata_end-testdata)  ; size
  mov dx, testdata   ; ptr to data
  int 0x21

  pop bx             ; file handle
  mov ah, 0x3e       ; close
  int 0x21  

quit:
  mov ah,0x4c        ; quit
  int 0x21

pipename:
  db "\\.\pipe\Test1",0

testdata:
  db "!", 0x22, "#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
testdata_end:
0 голосов
/ 18 июня 2010

Первое, что я бы попробовал изменить, это здесь:

            do
            {
                // !!! problematic function call here !!!
                int BytesRead = NPSS.Read(PipeDataBuffer, 0, PipeDataBuffer.Length);
                // !!!
                MessageStream.Write(PipeDataBuffer, 0, BytesRead);
                TotalBytesRead += BytesRead;
            } while (!NPSS.IsMessageComplete);

Он не проверяет, что BytesRead равно + ve; теперь я бы ожидал, что MessageStream.Write взорвется, если он будет отрицательным, но ... в любом случае; Я бы определенно проверил бы наличие неожиданного состояния <=0.

...