How to make a named pipe not busy after client has disconnected?

前端 未结 2 1646
旧巷少年郎
旧巷少年郎 2021-02-06 17:10

I use a named pipe and I want to reuse the same pipe on the server to allow connecting another client once the original client has disconnected. What I do is:

  • serv
相关标签:
2条回答
  • 2021-02-06 17:46

    The problem is that you've left out ConnectNamedPipe(), which should be always be called after CreateNamedPipe() or DisconnectNamedPipe() but before attempting any I/O.

    If you don't want to block while waiting for a client to connect, you can create the pipe in asynchronous I/O mode, in which case the call to ConnectNamedPipe() requires an event object which will be set when a client connects. Alternatively, you can set PIPE_NOWAIT and call ConnectNamedPipe() periodically until it succeeds, but this is a legacy feature and its use is discouraged. (In most situations using an event object will also be significantly more efficient than polling.)

    As you've discovered, Windows does allow you to get away without the call to ConnectNamedPipe() but since this behaviour is undocumented it should probably be avoided. Similarly, the fact that calling ConnectNamedPipe() without waiting for it to succeed resets the connection state of the pipe is undocumented and should not be depended upon.


    As requested, here's some real-world code to demonstrate the use of the server end of a pipe. This code was taken from a GUI application, so it uses asynchronous I/O, but it should be noted that it only talks to one client at a time. (It could however be run in multiple threads with only minor modifications.)

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

    (This code has been edited down from the original to remove extraneous logic. I haven't tried compiling the edited version, so there may be some minor problems.)

    0 讨论(0)
  • Experimenting with various calls, I have found following to work fine:

    In reaction to ERROR_PIPE_NOT_CONNECTED, server should perform:

      // 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 makes the pipe connectable (not busy) again.

    Note: pipe state is changed temporarily to PIPE_NOWAIT, as otherwise ConnectNamedPipe blocks the server thread waiting for the client infinitely.

    Other solution could probably be to close the handle completely on the server side and open it again.

    0 讨论(0)
提交回复
热议问题