检测命名管道断开连接 I/O 完成
Detecting named pipe disconnects with I/O completion
我对使用具有 I/O 完成端口的命名管道检测客户端断开连接的正确方法有疑问。我们有一个服务器创建子进程 stdin/stdout 重定向到命名管道。管道已打开 OVERLAPPED
。
我们已经看到,在客户端发出 CreateFile()
I/O 完成端口之后
收到一个 lpNumberOfBytes
为零的数据包——这非常有效地表明来自客户端的连接。但是检测子进程何时关闭其 stdin/stdout 并退出不会生成类似的事件。
我们提出了两种检测命名管道断开连接的方法;
1) 定期轮询子进程的进程HANDLE,检测进程何时结束,
或
2) 创建一个单独的线程,该线程在子进程的 HANDLE 上阻塞 WaitForSingleObject()
,当它发出信号时进程已结束,然后生成 PostQueuedCompletionStatus()
到 I/O 完成预先安排 COMPLETION_KEY.
的端口
这些都不难 -- 但我想确保我没有遗漏一些明显的东西。当与 IOCP 关联的命名管道已关闭时,有人找到替代通知的方法吗?
好的,我发现了为什么 IOCP 没有传送断开连接的数据包,这与我测试问题的方式有关。我们开发了一个单元测试工具,我们的单元测试同时充当服务器和客户端。当 child 进程结束时,child 的 write-pipe 句柄在单元测试中仍然打开,因此 IOCP 没有解除阻塞任何处理程序线程。
要有效 运行 管道服务器需要您创建一个新线程,并在该线程内完成连接到管道的工作,创建 child 进程并等待进程结束.在 child 结束后关闭管道句柄,这会导致 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();
}
我对使用具有 I/O 完成端口的命名管道检测客户端断开连接的正确方法有疑问。我们有一个服务器创建子进程 stdin/stdout 重定向到命名管道。管道已打开 OVERLAPPED
。
我们已经看到,在客户端发出 CreateFile()
I/O 完成端口之后
收到一个 lpNumberOfBytes
为零的数据包——这非常有效地表明来自客户端的连接。但是检测子进程何时关闭其 stdin/stdout 并退出不会生成类似的事件。
我们提出了两种检测命名管道断开连接的方法;
1) 定期轮询子进程的进程HANDLE,检测进程何时结束,
或
2) 创建一个单独的线程,该线程在子进程的 HANDLE 上阻塞 WaitForSingleObject()
,当它发出信号时进程已结束,然后生成 PostQueuedCompletionStatus()
到 I/O 完成预先安排 COMPLETION_KEY.
这些都不难 -- 但我想确保我没有遗漏一些明显的东西。当与 IOCP 关联的命名管道已关闭时,有人找到替代通知的方法吗?
好的,我发现了为什么 IOCP 没有传送断开连接的数据包,这与我测试问题的方式有关。我们开发了一个单元测试工具,我们的单元测试同时充当服务器和客户端。当 child 进程结束时,child 的 write-pipe 句柄在单元测试中仍然打开,因此 IOCP 没有解除阻塞任何处理程序线程。
要有效 运行 管道服务器需要您创建一个新线程,并在该线程内完成连接到管道的工作,创建 child 进程并等待进程结束.在 child 结束后关闭管道句柄,这会导致 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();
}