.NET NamedPipeServerStream 问题 - 连续读取返回相同的数据
我在使用 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 次:
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 客户端偶尔会出现 WriteFile 的 CANCELED 结果,而 Windows 客户端则不会。有点问题,因为这应该是 DOS 程序的插件:(
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
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
andEndRead
calls instead ofRead
- converting the code to be entirely synchronous, using just
WaitForConnection
andRead
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 :(
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我要尝试更改的第一件事是:
它不检查 BytesRead 是否为 +ve;现在我希望期望
MessageStream.Write
如果它是负数就会爆炸,但是......无论哪种方式;我肯定会检查那里是否存在意外的<=0
情况。The first thing I'd try changing is here:
It doesn't check that
BytesRead
is +ve; now I would expectMessageStream.Write
to explode if it was negative, but... either way; I would definitely check for an unexpected<=0
condition there.我想通了。
(这在 NamedPipeServerStream 中不是问题。)
看起来这是由于 DOS 系统调用选择不当造成的。如果我通过 INT 0x21 / AH=0x3D 使用 open 调用,则它可以正常工作。进程监视器中的堆栈跟踪也显示了此方法的非常不同的代码路径。这一切都很奇怪,但至少它有效。我想这个技术信息对世界上没有其他人有用,但我想我还是会更新:)
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 :)