【问题标题】:Detecting named pipe disconnects with I/O completion检测命名管道与 I/O 完成断开连接
【发布时间】:2016-02-04 00:14:14
【问题描述】:

我有一个关于使用具有 I/O 完成端口的命名管道检测客户端断开连接的正确方法的问题。我们有一个服务器可以创建子进程,并将标准输入/标准输出重定向到命名管道。管道已打开OVERLAPPED

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

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

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

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

这些都不难——但我想确保我没有遗漏一些明显的东西。当与 IOCP 关联的命名管道已关闭时,是否有人找到了通知的替代方法?

【问题讨论】:

    标签: winapi named-pipes iocp


    【解决方案1】:

    好的,我发现了 IOCP 不发送断开数据包的原因,这与我测试问题的方式有关。我们开发了一个单元测试工具,我们的单元测试既充当服务器又充当客户端。当子进程结束时,子进程的 write-pipe 句柄在 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();
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多