我正在为Windows编写一个简单的命名pipe道服务器,调用Windows API(Java中与JNA,但这是不相关的)。
我想弄清楚如何避免服务器永远等待客户端连接或数据来自客户端。
服务器代码执行以下操作:
1)通过在dwPipeMode
参数中调用CreateNamedPipe
和PIPE_WAIT
来创buildpipe道。
2)调用ConnectNamedPipe
,直到客户端连接才返回。
3)它进入一个循环,它通过调用ReadFile
重复读取消息, ReadFile
不会返回,直到读取数据为止。对于每个接收到的消息,它通过调用WriteFile
将消息发送回客户端。
4)经过许多这样的对话之后,客户端和服务器将从pipe道上断开。
我只是想能够设置在第2步的ConnectNamedPipe
和第3步的ReadFile
等待超时,我看不到在哪里设置超时。 CreateNamedPipe
函数中有nDefaultTimeOut
参数,但它并不是真正意义上的; API文档说:
如果WaitNamedPipe
函数指定NMPWAIT_USE_DEFAULT_WAIT
,则默认超时值(以毫秒为单位) 。
因此, CreateNamedPipe
的nDefaultTimeOut
arg听起来像默认的超时,连接到pipe道的客户端将使用它们的操作,并且只有在它们调用WaitNamedPipe
函数时。 实际上,在我的testing中,0或1000的值没有什么不同,对ConnectNamedPipe
的调用永远不会返回(除非客户端连接)。 我正在寻找的是超时服务器,而不是在ConnectNamedPipe
和ReadFile
的调用。
作为CreateNamedPipe
的文档,对于带有PIPE_WAIT
的dwPipeMode
参数PIPE_WAIT
, 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.
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
),以便对ReadFile
, WriteFile
和ConnectNamedPipe
调用立即返回,然后以某种方式监视自己的事件(客户端连接或数据接收),并在循环中检查自己是否超时或发生了另一个中断事件(如用户单击“取消”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不鼓励使用此选项。