.NET NamedPipeServerStream问题 - 连续读取返回相同的数据

时间:2010-06-18 19:47:34

标签: c# .net asynchronous named-pipes

我遇到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个字节在开头重复。

到目前为止,我尝试过改变:

  • 从消息到字节的传输模式
  • 在不同的主机上运行客户端和服务器,而不仅仅是本地
  • 使用异步BeginReadEndRead来代替Read
  • 仅使用WaitForConnectionRead
  • 将代码转换为完全同步

但这些都没有对问题产生任何影响。

任何人都可以了解我在这里做错了什么,或者我可以检查的其他事情?谢谢!

编辑:进一步研究 - 有所不同的是使用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汇编程序代码,实际上行为正常。

运行进程监视器在测试客户端运行时显示DOS偶尔会有对于WriteFile的CANCELED结果,而Windows客户端则没有。有点问题因为这应该是DOS程序的插件:(

2 个答案:

答案 0 :(得分:0)

我尝试改变的第一件事就是:

            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条件。

答案 1 :(得分:0)

我明白了。

(这在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: