我使用命名管道,我想在服务器上重复使用相同的管道,以便在原始客户端断开连接后允许连接另一个客户端。我所做的是:
CreateNamedPipe
WriteFile
写入数据,只要返回错误ERROR_PIPE_LISTENING
(在任何客户端连接之前),重试就会执行此操作。CreateFile
CloseHandle
ERROR_NO_DATA
DisconnectNamedPipe
断开管道,我希望它可以再次免费使用ERROR_PIPE_NOT_CONNECTED
,重试这样做直到没有错误CreateFile
时,它会获得ERROR_PIPE_BUSY
因此,我的问题是:我需要做些什么其他步骤来正确断开客户端与管道的连接,以便新客户端可以连接?
答案 0 :(得分:2)
尝试各种调用,我发现以下工作正常:
响应ERROR_PIPE_NOT_CONNECTED
,服务器应该执行:
// allow connecting, no wait
DWORD mode = PIPE_NOWAIT;
SetNamedPipeHandleState(_callstackPipe,&mode,NULL,NULL);
ConnectNamedPipe(_callstackPipe,NULL);
mode = PIPE_WAIT;
SetNamedPipeHandleState(_callstackPipe,&mode,NULL,NULL);
ConnectNamedPipe
使管道可以连接(不忙)。
注意:管道状态暂时更改为PIPE_NOWAIT
,否则ConnectNamedPipe
将无限制地阻塞等待客户端的服务器线程。
其他解决方案可能是完全关闭服务器端的句柄并再次打开它。
答案 1 :(得分:2)
问题是你已经遗漏了ConnectNamedPipe(),在 CreateNamedPipe()或DisconnectNamedPipe()之后应始终被称为,而尝试之前 I / O。
如果您不想在等待客户端连接时阻塞,可以在异步I / O模式下创建管道,在这种情况下,对ConnectNamedPipe()的调用需要一个将被设置的事件对象当客户端连接时。或者,您可以设置PIPE_NOWAIT并定期调用ConnectNamedPipe()直到成功,但这是一项遗留功能,不鼓励使用它。 (在大多数情况下,使用事件对象也将比轮询更有效。)
正如您所发现的,Windows确实允许您在没有调用ConnectNamedPipe()的情况下离开,但由于此行为未记录,因此应该避免使用它。类似地,调用ConnectNamedPipe()而不等待它成功重置管道的连接状态的事实是未记录的,不应该依赖它。
根据要求,这里有一些真实的代码来演示管道服务器端的使用。此代码取自GUI应用程序,因此它使用异步I / O,但应注意它一次只与一个客户端进行通信。 (但它可以在多个线程中运行,只需稍作修改。)
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 asynchronously.", 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 asynchronously.", 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 asynchronously.", 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();
}
}
}
(此代码已从原版中删除,以删除无关的逻辑。我还没有尝试编译已编辑的版本,因此可能存在一些小问题。)