通过I / O完成检测命名管道断开连接

时间:2016-02-04 00:14:15

标签: winapi named-pipes iocp

我对使用带有I / O完成端口的命名管道检测客户端断开连接的正确方法有疑问。我们有一个服务器,它创建子进程,并将stdin / stdout重定向到命名管道。管道打开OVERLAPPED

我们已经看到客户端发出CreateFile() I / O完成端口后 接收lpNumberOfBytes为零的数据包 - 这可以非常有效地指示来自客户端的连接。但是检测子进程何时关闭其stdin / stdout并退出并不会产生类似的事件。

我们提出了两种检测命名管道断开的方法;

1)定期轮询子进程的进程HANDLE,以检测进程何时结束,

OR

2)创建一个单独的线程,在子进程的HANDLE上阻塞WaitForSingleObject(),当它发出信号表示进程已经结束时,然后生成PostQueuedCompletionStatus()到预先安排的I / O完成端口COMPLETION_KEY。

这些都不困难 - 但我想确保我没有遗漏任何明显的东西。有没有人发现当与IOCP关联的命名管道被关闭时收到通知的替代方法?

1 个答案:

答案 0 :(得分:0)

好的,我发现为什么IOCP没有提供断开连接数据包,而且我已经知道我是如何测试这个问题的。我们开发了一个单元测试工具,我们的单元测试既是服务器又是客户端。当子进程结束时,子进程的写管句柄仍然在unittest中打开,因此IOCP没有解除任何处理程序线程的阻塞。

要有效地运行管道服务器,需要创建一个新线程并在该线程内执行连接管道的工作,创建子进程并等待进程结束。在子进程结束之后关闭管道句柄,导致IOCP然后发送一个lpNumberOfBytes设置为零的出列数据包。

以下是我们如何使用_beginthread()创建的线程的示例。

void __cdecl childproc(void* p) {

    TCHAR* pipename = (TCHAR*)p;

    /* make sure pipe handle is "inheritable" */
    SECURITY_ATTRIBUTES sattr;
    sattr.nLength = sizeof(SECURITY_ATTRIBUTES);
    sattr.bInheritHandle = TRUE;
    sattr.lpSecurityDescriptor = NULL;

    HANDLE pipe = ::CreateFile(
                        pipename,
                        GENERIC_READ | GENERIC_WRITE,
                        0,
                        &sattr,
                        OPEN_EXISTING,
                        FILE_ATTRIBUTE_NORMAL,
                        NULL);
    if (pipe == INVALID_HANDLE_VALUE) {
        _tprintf(_T("connect to named pipe failed %ld\n", GetLastError());
        _endthread();
        }

    /* redirect stdin/stdout/stderr to pipe */
    PROCESS_INFORMATION procinfo;
    STARTUPINFO startinfo;
    memset(&procinfo, 0, sizeof(procinfo));
    memset(&startinfo, 0, sizeof(startinfo));
    startinfo.cb = sizeof(startinfo);
    startinfo.hStdError = pipe;
    startinfo.hStdOutput = pipe;
    startinfo.hStdInput = pipe;
    startinfo.dwFlags |= STARTF_USESTDHANDLES;

    /* create child to do a simple "cmd.exe /c dir" */
    DWORD rc = ::CreateProcess(
                    _T("C:\\Windows\\System32\\cmd.exe"),
                    _T("C:\\Windows\\System32\\cmd.exe /C dir"),
                    NULL,
                    NULL,
                    TRUE,
                    0,
                    NULL,
                    NULL,
                    &startinfo,
                    &procinfo);
    if (rc == 0) {
        _tprintf(_T("cannot create child process: %ld\n"), GetLastError());
        _endthread();
        }
    if (::WaitForSingleObject(procinfo.hProcess, INFINITE) != WAIT_OBJECT_0) {
        _tprintf(_T("error waiting for child to end: %ld\n"), GetLastError());
        }

    /* cleanup */
    ::CloseHandle(procinfo.hProcess);
    ::CloseHandle(procinfo.hThread);
    ::CloseHandle(pipe);

    _endthread();
    }