2009-07-28 132 views
2

我使用了一個命名管道,並且我想在服務器上重複使用相同的管道,以便在原始客戶端斷開連接後允許連接另一個客戶端。我要做的就是:如何在客戶端斷開連接後使命名管道不忙?

  • 服務器創建使用CreateNamedPipe
  • 服務器管道使用WriteFile寫入數據,並返回重試所以只要做是錯誤ERROR_PIPE_LISTENING(這是任何客戶端連接之前)
  • 客戶連接使用CreateFile
  • 客戶在這一點上讀取數據
  • 客戶端關閉管道處理使用CloseHandle
  • 服務器收到錯誤​​當attemps寫入更多的數據
  • 服務器使用DisconnectNamedPipe,我希望應該讓它重獲自由
  • 服務器嘗試寫入數據時,被錯誤ERROR_PIPE_NOT_CONNECTED,它重試這樣做,直到沒有錯誤斷開的管道
  • 然而,當新的客戶端連接,並在管道試圖CreateFile,它得到ERROR_PIPE_BUSY

因此,我的問題是:我需要哪些步驟來做到正確地從管道斷開連接客戶端,以便新客戶端可以連接?

回答

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

此行爲是未記錄的。最好等到ConnectNamedPipe()實際上成功後,通過輪詢或使用異步I/O。 (請參閱我的答案。) – 2016-03-06 23:03:19

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

(此代碼已經從原來的編輯了下來,除去多餘的邏輯。我沒試過編譯編輯的版本,所以有可能是一些小問題。)

+0

也許有些代碼示例會更易讀? – Suma 2016-03-07 09:23:46