开发者

.NET NamedPipeServerStream problem - consecutive Reads return the same data

开发者 https://www.devze.com 2023-01-04 04:05 出处:网络
I\'m having a problem with NamedPipeServerStream - when my code reads data it\'s just repeating the output of the last Read without grabbing the new data.

I'm having a problem with NamedPipeServerStream - when my code reads data it's just repeating the output of the last Read without grabbing the new data.

Here's the smallest server code example that exhibits this behaviour:

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
  开发者_Go百科              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);
        }
    }
}

This is the test client (written in DOS assembler, NASM-style, compiles to .COM file). All it does is opens the pipe as a file (\.\pipe\Test1) and writes some data to it:

; 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:

And here is a typical output of the server, in this case from running the client three times in succession:

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{|}~

As you can see, the first message is 16 bytes (i.e. one buffer length) longer than it should be, because the first 16 bytes is repeated at the beginning.

So far I've tried changing:

  • the transmission mode from message to byte
  • running the client and server on different hosts rather than just local
  • using the async BeginRead and EndRead calls instead of Read
  • converting the code to be entirely synchronous, using just WaitForConnection and Read

but none of this made any difference to the problem.

Can anyone please shed any light on what I'm doing wrong here, or other things I can check? Thanks!

EDIT: Further research - what has made a difference is using a Windows test client rather than one running under the NTVDM (DOS machine). That is, this code:

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;
}

which should be equivalent to the DOS assembler code above, actually behaves properly.

Running Process Monitor when the test clients are running shows that the DOS one occasionally has a result of CANCELLED for WriteFile, whereas the Windows client does not. A bit of a problem as this is supposed to be an addon for a DOS program :(


The first thing I'd try changing is here:

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

It doesn't check that BytesRead is +ve; now I would expect MessageStream.Write to explode if it was negative, but... either way; I would definitely check for an unexpected <=0 condition there.


I figured it out.

( it wasn't a problem in NamedPipeServerStream.)

Looks like it was due to a poor choice of DOS system calls. If I use the open call via INT 0x21 / AH=0x3D, it works without problems. The stack trace in Process Monitor shows a very different code path for this method too. It's all rather odd, but at least it's working. I guess this technical information is useful to no-one else in the world but I thought I'd update anyway :)

; 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

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号