gpt4 book ai didi

winapi - 客户端断开连接后如何使命名管道不忙?

转载 作者:行者123 更新时间:2023-12-04 00:56:32 29 4
gpt4 key购买 nike

我使用命名管道,我想在服务器上重用同一个管道,以便在原始客户端断开连接后连接另一个客户端。我所做的是:

  • 服务器使用 CreateNamedPipe 创建管道
  • 服务器使用 WriteFile 写入数据,并在返回错误 ERROR_PIPE_LISTENING 时重试(这是在任何客户端连接之前)
  • 客户端使用 CreateFile 连接
  • 客户端读取数据
  • 客户端使用 CloseHandle 关闭管道句柄
  • 此时服务器在尝试写入更多数据时收到错误ERROR_NO_DATA
  • 服务器使用 DisconnectNamedPipe 断开管道连接,我希望它能再次释放
  • 服务器尝试写入数据,收到错误 ERROR_PIPE_NOT_CONNECTED,它会重试,直到没有错误为止
  • 但是,当新客户端连接并尝试在管道上CreateFile 时,它会得到ERROR_PIPE_BUSY

因此,我的问题是:我还需要执行哪些其他步骤才能正确断开客户端与管道的连接,以便新客户端可以连接?

最佳答案

问题是您遗漏了 ConnectNamedPipe(),它应该始终在CreateNamedPipe() 或 DisconnectNamedPipe() 之后尝试任何 I/哦。

如果您不想在等待客户端连接时阻塞,您可以在异步 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();
}
}
}

(此代码已从原始代码中删除了多余的逻辑。我没有尝试编译编辑后的版本,因此可能存在一些小问题。)

关于winapi - 客户端断开连接后如何使命名管道不忙?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1193141/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com