.NET NamedPipeServerStream problem - consecutive Reads return the same 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
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 :(
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:
精彩评论