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

时间:2009-07-28 10:04:21

标签: winapi named-pipes

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

  • 服务器使用CreateNamedPipe
  • 创建管道
  • 服务器使用WriteFile写入数据,只要返回错误ERROR_PIPE_LISTENING(在任何客户端连接之前),重试就会执行此操作。
  • 客户使用CreateFile
  • 进行连接
  • 客户端读取数据
  • 客户端使用CloseHandle
  • 关闭管道句柄
  • 此时服务器在尝试写入更多数据时会收到错误ERROR_NO_DATA
  • 服务器使用DisconnectNamedPipe断开管道,我希望它可以再次免费使用
  • 服务器尝试写入数据,获取错误ERROR_PIPE_NOT_CONNECTED,重试这样做直到没有错误
  • 但是,当新客户端连接并在管道上尝试CreateFile时,它会获得ERROR_PIPE_BUSY

因此,我的问题是:我需要做些什么其他步骤来正确断开客户端与管道的连接,以便新客户端可以连接?

2 个答案:

答案 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();
        }
    }
}

(此代码已从原版中删除,以删除无关的逻辑。我还没有尝试编译已编辑的版本,因此可能存在一些小问题。)