命名pipe道服务器,如何中断或超时等待客户端连接和传入数据

我正在为Windows编写一个简单的命名pipe道服务器,调用Windows API(Java中与JNA,但这是不相关的)。

我想弄清楚如何避免服务器永远等待客户端连接或数据来自客户端。

服务器代码执行以下操作:

1)通过在dwPipeMode参数中调用CreateNamedPipePIPE_WAIT来创buildpipe道。

2)调用ConnectNamedPipe ,直到客户端连接才返回。

3)它进入一个循环,它通过调用ReadFile重复读取消息, ReadFile不会返回,直到读取数据为止。对于每个接收到的消息,它通过调用WriteFile将消息发送回客户端。

4)经过许多这样的对话之后,客户端和服务器将从pipe道上断开。

我只是想能够设置在第2步的ConnectNamedPipe和第3步的ReadFile等待超时,我看不到在哪里设置超时。 CreateNamedPipe函数中有nDefaultTimeOut参数,但它并不是真正意义上的; API文档说:

如果WaitNamedPipe函数指定NMPWAIT_USE_DEFAULT_WAIT ,则默认超时值(以毫秒为单位)

因此, CreateNamedPipenDefaultTimeOut arg听起来像默认的超时,连接到pipe道的客户端将使用它们的操作,并且只有在它们调用WaitNamedPipe函数时。 实际上,在我的testing中,0或1000的值没有什么不同,对ConnectNamedPipe的调用永远不会返回(除非客户端连接)。 我正在寻找的是超时服务器,而不是在ConnectNamedPipeReadFile的调用。

作为CreateNamedPipe的文档,对于带有PIPE_WAITdwPipeMode参数PIPE_WAITBlocking mode is enabled. When the pipe handle is specified in the ReadFile, WriteFile, or ConnectNamedPipe function, the operations are not completed until there is data to read, all data is written, or a client is connected. Use of this mode can mean waiting indefinitely in some situations for a client process to perform an action. Blocking mode is enabled. When the pipe handle is specified in the ReadFile, WriteFile, or ConnectNamedPipe function, the operations are not completed until there is data to read, all data is written, or a client is connected. Use of this mode can mean waiting indefinitely in some situations for a client process to perform an action.

所以也许实现这种超时的方法是以非阻塞模式创buildpipe道(使用PIPE_NOWAIT而不是PIPE_WAIT ),以便对ReadFileWriteFileConnectNamedPipe调用立即返回,然后以某种方式监视自己的事件(客户端连接或数据接收),并在循环中检查自己是否超时或发生了另一个中断事件(如用户单击“取消”button)?

增加 :它看起来像ReadFile调用我可能能够使用PeekNamedPipe立即返回,检查是否有数据要读取,然后调用ReadFile 。 我会尝试的。 但是对于ConnectNamedPipe的调用,我仍然ConnectNamedPipe同样的问题。

补充 :据我怀疑和答案证实,作为pipe道的新手,我从一个不知何故倾斜的angular度看他们,从中超时的需要出现比实际。

F.ex. 想要超时调用ReadFile是,如果我(服务器)在里面,它从客户端读取数据,而客户端突然closures,有时候我可能会被卡在ReadFile 。 但是现在我知道如果ReadFile正在从pipe道中读取并且客户端closures,那么ReadFile总是出错,所以执行不会被卡住。

在GUI应用程序中演示管道服务器端异步使用的一些真实世界的代码:

 void wait_for_object(HANDLE object) { DWORD dw; MSG msg; for (;;) { dw = MsgWaitForMultipleObjectsEx(1, &object, INFINITE, QS_ALLINPUT, 0); if (dw == WAIT_OBJECT_0) break; if (dw == WAIT_OBJECT_0 + 1) { while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) DispatchMessage(&msg); continue; } srvfail(L"sleep() messageloop", GetLastError()); } } HANDLE server_pipe; HANDLE io_event; void pipe_connection(void) { OVERLAPPED overlapped; DWORD dw, err; SecureZeroMemory(&overlapped, sizeof(overlapped)); overlapped.hEvent = io_event; if (!ReadFile(server_pipe, input_buffer, sizeof(input_buffer) - 1, NULL, &overlapped)) { err = GetLastError(); if (err == ERROR_IO_PENDING) { wait_for_object(io_event); if (!GetOverlappedResult(server_pipe, &overlapped, &dw, FALSE)) { srvfail(L"Read from pipe failed asynchronousously.", GetLastError()); } } else { srvfail(L"Read from pipe failed synchronously.", GetLastError()); } } else { if (!GetOverlappedResult(server_pipe, &overlapped, &dw, FALSE)) { srvfail(L"GetOverlappedResult failed reading from pipe.", GetLastError()); } } input_buffer[dw] = '\0'; process_command(); if (!WriteFile(server_pipe, &output_struct, ((char *)&output_struct.output_string - (char *)&output_struct) + output_struct.string_length, NULL, &overlapped)) { err = GetLastError(); if (err == ERROR_IO_PENDING) { wait_for_object(io_event); if (!GetOverlappedResult(server_pipe, &overlapped, &dw, FALSE)) { srvfail(L"Write to pipe failed asynchronousously.", GetLastError()); } } else { srvfail(L"Write to pipe failed synchronously.", GetLastError()); } } else { if (!GetOverlappedResult(server_pipe, &overlapped, &dw, FALSE)) { srvfail(L"GetOverlappedResult failed writing to pipe.", GetLastError()); } } if (!FlushFileBuffers(server_pipe)) srvfail(L"FlushFileBuffers failed.", GetLastError()); if (!DisconnectNamedPipe(server_pipe)) srvfail(L"DisconnectNamedPipe failed.", GetLastError()); } void server(void) { OVERLAPPED overlapped; DWORD err, dw; // Create the named pipe server_pipe = CreateNamedPipe(pipe_name, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE | FILE_FLAG_OVERLAPPED, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, 1, buffer_size, buffer_size, 0, NULL); if (server_pipe == INVALID_HANDLE_VALUE) srvfail(L"CreateNamedPipe failed.", GetLastError()); // Wait for connections io_event = CreateEvent(NULL, FALSE, FALSE, NULL); if (io_event == NULL) srvfail(L"CreateEvent(io_event) failed.", GetLastError()); for (;;) { SecureZeroMemory(&overlapped, sizeof(overlapped)); overlapped.hEvent = io_event; if (!ConnectNamedPipe(server_pipe, &overlapped)) { err = GetLastError(); if (err == ERROR_PIPE_CONNECTED) { pipe_connection(); } else if (err == ERROR_IO_PENDING) { wait_for_object(io_event); if (!GetOverlappedResult(server_pipe, &overlapped, &dw, FALSE)) { srvfail(L"Pipe connection failed asynchronousously.", GetLastError()); } pipe_connection(); } else { srvfail(L"Pipe connection failed synchronously.", GetLastError()); } } else { if (!GetOverlappedResult(server_pipe, &overlapped, &dw, FALSE)) { srvfail(L"GetOverlappedResult failed connecting pipe.", GetLastError()); } pipe_connection(); } } } 

(这个代码已经从原来的编辑下来去除了无关的逻辑,我还没有试过编译编辑过的版本,所以可能会有一些小问题,还要注意全局变量的使用,这对我来说是可以的,因为应用程序是非常小的,但通常应该避免。)

使用MsgWaitForMultipleObjectsEx()允许在等待I / O完成时处理窗口消息。 如果你还在等待其他的事情发生,你可以传递一个句柄数组而不是一个句柄 – 例如,如果你想监视一个子进程,并在退出时做一些事情,你可以传递一个数组io_event和进程句柄。 如果你只是定期做一些其他的工作,你可以设置等待超时,或使用窗口计时器。

我建议你设置FILE_FLAG_OVERLAPPED并使用一个事件来检查/等待完成。

尽管最初是为异步IO设计的,但您也可以将事件定时到预定义的时间。

如果您想要取消I / O操作,则可以使用CancelIo()函数。 如果你只是想做一些工作,然后继续等待,你也可以这样做 – 超时等待不会自动取消I / O,所以你不需要再次调用ConnectNamedPipe。

你也可以像你自己建议的那样设置PIPE_NOWAIT并且轮询连接直到成功,在这种用例中任何一种方法都应该带来相同的结果。 但请注意,这是传统功能,Microsoft不鼓励使用此选项。