PIPE_NOWAIT 断开连接

PIPE_NOWAIT breaks connetion

我有一段代表简单管道服务器的代码。当我设置 PIPE_NOWAIT 属性然后尝试使用 CreateFile 函数从客户端连接到它时 - 没有任何反应,我们仍在第二个块中。当我删除 PIPE_NOWAIT 标志时 - 我们挂在 ConnectNamedPipe 函数中,在从客户端调用 CreateFile 之后,我们进入了第一个块,这意味着我们成功了。为什么会这样?

HANDLE hPipe;
std::string msg;
DWORD dwRead;

hPipe = CreateNamedPipe(L"\\.\pipe\Pipe100500",
    PIPE_ACCESS_DUPLEX,   // FILE_FLAG_FIRST_PIPE_INSTANCE is not needed but forces CreateNamedPipe(..) to fail if the pipe already exists...
    PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_NOWAIT,
    1,
    1024 * 16,
    1024 * 16,
    NMPWAIT_USE_DEFAULT_WAIT,
    NULL);

while (true)
{
    if (ConnectNamedPipe(hPipe, NULL) != FALSE)
    {
        // Waiting in the ConnectNamedPipe and then we are here
    }
    else
    {
        // always here when PIPE_NOWAIT
    }
}

根据 CreateNamedPipe() 文档:

PIPE_NOWAIT
0x00000001

Nonblocking mode is enabled. In this mode, ReadFile, WriteFile, and ConnectNamedPipe always return immediately.

根据 ConnectNamedPipe() 文档:

If the specified pipe handle is in nonblocking mode, ConnectNamedPipe always returns immediately. In nonblocking mode, ConnectNamedPipe returns a nonzero value the first time it is called for a pipe instance that is disconnected from a previous client. This indicates that the pipe is now available to be connected to a new client process. In all other situations when the pipe handle is in nonblocking mode, ConnectNamedPipe returns zero. In these situations, GetLastError returns ERROR_PIPE_LISTENING if no client is connected, ERROR_PIPE_CONNECTED if a client is connected, and ERROR_NO_DATA if a previous client has closed its pipe handle but the server has not disconnected. Note that a good connection between client and server exists only after the ERROR_PIPE_CONNECTED error is received.

Note Nonblocking mode is supported for compatibility with Microsoft LAN Manager version 2.0, and it should not be used to achieve asynchronous input and output (I/O) with named pipes.

因此,要正确使用 PIPE_NOWAIT(即使您真的根本不应该使用它!),您还需要这样的东西:

HANDLE hPipe;
std::string msg;
DWORD dwRead;

hPipe = CreateNamedPipe(L"\\.\pipe\Pipe100500",
    PIPE_ACCESS_DUPLEX,   // FILE_FLAG_FIRST_PIPE_INSTANCE is not needed but forces CreateNamedPipe(..) to fail if the pipe already exists...
    PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_NOWAIT,
    1,
    1024 * 16,
    1024 * 16,
    NMPWAIT_USE_DEFAULT_WAIT,
    NULL);

if (hPipe != INVALID_HANDLE_VALUE)
{
    while (true)
    {
        if (ConnectNamedPipe(hPipe, NULL))
        {
            // pipe has been disconnected and is now available to be connected to again...
        }
        else
        {
            switch (GetLastError())
            {
                case ERROR_PIPE_LISTENING:
                    // no client is connected...
                    break;

                case ERROR_PIPE_CONNECTED:
                   // a client is now connected...
                   break;

                case ERROR_NO_DATA:
                    // a previous client has closed its pipe handle but the server has not disconnected yet...
                    DisconnectNamedPipe(hPipe);
                    break;

                default:
                    // fatal error...
                    break;
            }
        }
    }
}

否则,按照文档中的说明进行操作,根本不要使用 PIPE_NOWAIT。如果您不想 ConnectNamedPipe() 阻塞,请改用 Overlapped I/O。 MSDN 提供了这样的示例:

Named Pipe Server Using Overlapped I/O

Named Pipe Server Using Completion Routines