views:

547

answers:

2

A Named Pipe Server is created with

hPipe = CreateNamedPipe( zPipePath,
                         PIPE_ACCESS_DUPLEX,
                         PIPE_TYPE_BYTE | PIPE_WAIT | PIPE_READMODE_BYTE,
                         PIPE_UNLIMITED_INSTANCES,
                         8192, 8192, NMPWAIT_USE_DEFAULT_WAIT, NULL)

Then we immediately call:

ConnectNamedPipe( hPipe, BYVAL %NULL )

Which blocks until the client connects.

Then we proceed directly to ReadFile( hPipe, ...

The problem is that it takes the Client takes a finite amount of time to prepare and write all the fcgi request parameters. This has usually not completed before the Pipe Server performs its ReadFile(). The Read file operation thus finds no data in the pipe and the process fails.

Is there a mechanism to tell when a Write() has occurred/finished after a client has connected to a NamedPipe?

If I had control of the Client process, I could use a common Mutex, but I don't, and I really do not want to get into I/O completion ports just to solve this problem!

I can of course use a simple timer to wait 60m/s or so which is usually plenty of time for the write to complete, but that is a horrible hack.

+2  A: 

I'm not sure what language you're using - I don't recognize the expression like

PIPE_TYPE_BYTE OR %PIPE_WAIT OR %PIPE_READMODE_BYTE

but it looks like to me that the server's ReadFile() should block until data is written by the client.

One thing that doesn't look right (but shouldn't be hurting anything) is the use of NMPWAIT_USE_DEFAULT_WAIT - that value is intended for a client's use when calling WaitNamedPipe().

Could you see if these 2 C++ programs interact like you expect (ie., with the server blocking until the client writes something)? I took these programs from the Jones/Ohlund book "Network Programming for Windows" The server blocks in the ReadFile() call for me as expected:

- server.cpp

// Server.cpp

#include <windows.h>
#include <stdio.h>

void main(void)
{
    HANDLE PipeHandle;
    DWORD BytesRead;
    CHAR buffer[256];
    if ((PipeHandle = CreateNamedPipe("\\\\.\\Pipe\\Jim",
        PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1,
        0, 0, 1000, NULL)) == INVALID_HANDLE_VALUE)
    {
        printf("CreateNamedPipe failed with error %d\n",
            GetLastError());
        return;
    }

    printf("Server is now running\n");

    if (ConnectNamedPipe(PipeHandle, NULL) == 0)
    {
        printf("ConnectNamedPipe failed with error %d\n",
            GetLastError());
        CloseHandle(PipeHandle);
        return;
    }

    if (ReadFile(PipeHandle, buffer, sizeof(buffer),
        &BytesRead,  NULL) <= 0)
    {
        printf("ReadFile failed with error %d\n", GetLastError());
        CloseHandle(PipeHandle);
        return;
    }

    printf("%.*s\n", BytesRead, buffer);

    if (DisconnectNamedPipe(PipeHandle) == 0)
    {
        printf("DisconnectNamedPipe failed with error %d\n",
            GetLastError());
        return;
    }

    CloseHandle(PipeHandle);
}

- client.cpp

// Client.cpp

#include <windows.h>
#include <stdio.h>

#define PIPE_NAME "\\\\.\\Pipe\\jim"

void main(void)
{
    HANDLE PipeHandle;
    DWORD BytesWritten;

    if (WaitNamedPipe(PIPE_NAME, NMPWAIT_WAIT_FOREVER) == 0)
    {
        printf("WaitNamedPipe failed with error %d\n",
            GetLastError());
        return;
    }

    // Create the named pipe file handle
    if ((PipeHandle = CreateFile(PIPE_NAME,
        GENERIC_READ | GENERIC_WRITE, 0,
        (LPSECURITY_ATTRIBUTES) NULL, OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        (HANDLE) NULL)) == INVALID_HANDLE_VALUE)
    {
        printf("CreateFile failed with error %d\n", GetLastError());
        return;
    }

    if (WriteFile(PipeHandle, "This is a test", 14, &BytesWritten, 
        NULL) == 0)
    {
        printf("WriteFile failed with error %d\n", GetLastError());
        CloseHandle(PipeHandle);
        return;
    }

    printf("Wrote %d bytes", BytesWritten);

    CloseHandle(PipeHandle);
}
Michael Burr
A: 

Thank you for your answer. Lets use C++ for continuity. Well this example is a little different because you are not using the PIPE_WAIT flag on the pipe server side.

As I understand it, the concept of that flag is that the

ConnectNamedPipe()

Will block until the Client connects. This is what we want.

Then the server ALSO needs to wait until the Client has written to the pipe. This is not happening with the PIPE_WAIT flag set.

I have no access to the code on the client side, so I am not able to know what is going on there, but since I am writing the Server side, I assume I should be able to control this behaviour?

Mike Trader
PIPE_WAIT is defined to be 0, so the fact that it's not in the dwPipeMode parameter is the same as it being there (ie., the important thing is that the parameter does *not* have PIPE_NOWAIT).
Michael Burr
Can you post the exact ReadFile() call you're using (including the code that initializes the arguments), how you're checking for a read failure, and what the GetLastError() value is when the read fails?
Michael Burr
Mike Trader
I'm confused - I thought the problem was that ReadFile() does not block, but now it sounds like you are never calling ReadFile().
Michael Burr
Sorry Michael, I assumed that PeekNamedPipe() would block in the same way that ReadFile() did. I assumed it was a thin wrapper for ReadFile() that does advance the current file position. Let me try re-writing the function.
Mike Trader
Hmm - I would have assumed that PeekNamedPipe() would never block. But it's blocking behavior turns out to be a bit weird - it'll never block in a single-threaded app, but can block in a multi-threaded app (according to the docs - I haven't tested this). Is that ever non-intuitive.
Michael Burr
can you contact me... see above
Mike Trader