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

Posted

技术标签:

【中文标题】.NET NamedPipeServerStream 问题 - 连续读取返回相同的数据【英文标题】:.NET NamedPipeServerStream problem - consecutive Reads return the same data 【发布时间】:2010-06-18 19:47:34 【问题描述】:

我在使用 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:

这是服务器的典型输出,在这种情况下是连续运行客户端 3 次:

按 X 退出 收到的消息,110 字节: !"#$%&'()*+,-./0!"#$%&'()*+,-./0123456789:;?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz|~ 收到的消息,94 字节: !"#$%&'()*+,-./0123456789:;?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz|~ 收到的消息,94 字节: !"#$%&'()*+,-./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 程序的插件 :(

【问题讨论】:

【参考方案1】:

我要尝试更改的第一件事是:

            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 如果它是负面的,就会爆炸,但是......无论哪种方式;我肯定会在那里检查意外的&lt;=0 条件。

【讨论】:

感谢您的建议 - 我添加了一些代码来打印 BytesRead 的值,并且每次都读取正确的数字,没有读取零字节或负字节。不幸的是,事实并非如此。【参考方案2】:

我想通了。

(在 NamedPipeServerStream 中这不是问题。)

看起来这是由于 DOS 系统调用选择不当造成的。如果我通过 INT 0x21 / AH=0x3D 使用 open 调用,它可以正常工作。 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:

【讨论】:

以上是关于.NET NamedPipeServerStream 问题 - 连续读取返回相同的数据的主要内容,如果未能解决你的问题,请参考以下文章

.NET平台系列26:在 Windows 上安装 .NET Core/.NET5/.NET6

[.NET大牛之路 005] .NET 的执行模型

ADO.NET和.NET的关系?

VS2022 安装.NET 3.5/.NET 4/.NET 4.5/.NET 4.5.1目标包的方法

.net core 3.0和.net5有什么区别

能说一下ADO.NET 和.NET,还有asp.NET的区别吗?