3

名前付きパイプを使用しており、サーバー上で同じパイプを再利用して、元のクライアントが切断された後に別のクライアントを接続できるようにしたいと考えています。私がすることは:

  • サーバーは使用してパイプを作成しますCreateNamedPipe
  • サーバーは を使用してデータを書き込みWriteFile、エラーが返される限り再試行しますERROR_PIPE_LISTENING(これはクライアントが接続される前です)。
  • クライアントは次を使用して接続しますCreateFile
  • クライアントがデータを読み取る
  • クライアント クローズ パイプ ハンドルを使用してCloseHandle
  • この時点で、サーバーがERROR_NO_DATAさらにデータを書き込もうとするとエラーが発生します
  • サーバーは を使用してパイプを切断します。これによりDisconnectNamedPipe、再び解放されるはずです
  • サーバーはデータの書き込みを試み、エラーを取得し、エラーERROR_PIPE_NOT_CONNECTEDがなくなるまで再試行します
  • ただし、新しいクライアントが接続CreateFileし、パイプで試行すると、取得されますERROR_PIPE_BUSY

したがって、私の質問は次のとおりです。クライアントをパイプから適切に切断して、新しいクライアントが接続できるようにするために必要な他の手順は何ですか?

4

2 に答える 2

3

問題は、常にCreateNamedPipe() または DisconnectNamedPipe()の、I/O を試行する前に呼び出す必要がある ConnectNamedPipe() を省略したことです。

クライアントが接続するのを待っている間にブロックしたくない場合は、非同期 I/O モードでパイプを作成できます。この場合、ConnectNamedPipe() の呼び出しには、クライアントの接続時に設定されるイベント オブジェクトが必要です。または、PIPE_NOWAIT を設定して、成功するまで定期的に ConnectNamedPipe() を呼び出すこともできますが、これはレガシー機能であり、使用しないことをお勧めします。(ほとんどの場合、イベント オブジェクトを使用すると、ポーリングよりもはるかに効率的になります。)

お気づきのとおり、Windows では ConnectNamedPipe() を呼び出さなくても問題を解決できますが、この動作は文書化されていないため、おそらく回避する必要があります。同様に、成功を待たずに ConnectNamedPipe() を呼び出すと、パイプの接続状態がリセットされるという事実は文書化されておらず、依存すべきではありません。


リクエストに応じて、パイプのサーバー側の使用を示す実際のコードを次に示します。このコードは GUI アプリケーションから取得したものであるため、非同期 I/O を使用していますが、一度に 1 つのクライアントとしか通信できないことに注意してください。(ただし、わずかな変更を加えるだけで、複数のスレッドで実行できます。)

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

(このコードは、余分なロジックを削除するために元のコードから編集されています。編集されたバージョンをコンパイルしようとはしていないため、小さな問題がいくつかある可能性があります。)

于 2016-03-06T23:02:31.003 に答える
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、サーバースレッドがクライアントを無限に待機するのをブロックします。

他の解決策は、おそらくサーバー側のハンドルを完全に閉じて、もう一度開くことです。

于 2009-07-28T10:13:19.210 に答える