2009-07-28 3 views
2

J'utilise un canal nommé et je souhaite réutiliser le même canal sur le serveur pour permettre la connexion d'un autre client une fois le client d'origine déconnecté. Ce que je fais est:Comment rendre un canal nommé non occupé après la déconnexion du client?

    serveur
  • crée un tuyau à l'aide CreateNamedPipe
  • serveur écrit des données à l'aide WriteFile et relances faisant, aussi longtemps que l'erreur ERROR_PIPE_LISTENING est retourné (qui est avant tout client est connecté)
  • clients connecte à l'aide CreateFile
  • client
  • lit les données
  • client tuyau à proximité poignée à l'aide CloseHandle
  • à ce stade serveur reçoit une erreur ERROR_NO_DATA quand il attemps d'écrire plus de données
  • serveur déconnecte le tuyau à l'aide DisconnectNamedPipe, que je l'espérais devrait le faire à nouveau libre
  • serveur tente d'écrire des données, reçoit une erreur ERROR_PIPE_NOT_CONNECTED, il réessaie faire jusqu'à ce qu'il n'y ait pas d'erreur
  • cependant, lorsque le nouveau client se connecte, et tente CreateFile sur le tuyau, il obtient ERROR_PIPE_BUSY

par conséquent, ma question est: ce que les autres mesures que je dois faire pour déconnecter le client de la conduite proprement qu'une nouvelle le client peut se connecter?

Répondre

2

Expérimenter différents appels, j'ai trouvé ci-dessous pour fonctionner correctement:

En réaction à ERROR_PIPE_NOT_CONNECTED, serveur doit effectuer:

// 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 rend le tuyau connectable (non occupé) à nouveau.

Remarque: l'état de la conduite est temporairement modifié en PIPE_NOWAIT, sinon ConnectNamedPipe bloque le thread serveur en attente infinie pour le client.

Une autre solution pourrait probablement être de fermer complètement la poignée côté serveur et de l'ouvrir à nouveau.

+1

Ce comportement est non documenté. Il serait préférable d'attendre que ConnectNamedPipe() réussisse, soit en interrogeant, soit en utilisant des E/S asynchrones. (Voir ma réponse.) –

2

Le problème est que vous avez laissé à ConnectNamedPipe(), qui devrait être appeler toujours après CreateNamedPipe() ou DisconnectNamedPipe() mais avant de tenter toute E/S. Si vous ne souhaitez pas bloquer en attendant qu'un client se connecte, vous pouvez créer le canal en mode d'E/S asynchrone, auquel cas l'appel à ConnectNamedPipe() nécessite un objet d'événement qui sera défini lorsque un client se connecte. Vous pouvez également définir PIPE_NOWAIT et appeler ConnectNamedPipe() périodiquement jusqu'à ce qu'il réussisse, mais il s'agit d'une fonctionnalité héritée et son utilisation est déconseillée. (Dans la plupart des cas, l'utilisation d'un objet événement sera également significativement plus efficace que l'interrogation.)

Comme vous l'avez découvert, Windows vous permet de vous en passer sans l'appel de ConnectNamedPipe() mais comme ce comportement n'est pas documenté, il devrait probablement être évité. De même, le fait que l'appel de ConnectNamedPipe() sans attendre la réinitialisation réinitialise l'état de connexion du canal n'est pas documenté et ne devrait pas dépendre de cela.


Comme demandé, voici un code réel pour démontrer l'utilisation de l'extrémité serveur d'un tube. Ce code a été pris à partir d'une application GUI, donc il utilise des E/S asynchrones, mais il convient de noter qu'il ne parle qu'à un client à la fois. (Il pourrait cependant être exécuté en plusieurs threads avec des modifications mineures.)

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

(Ce code a été modifié vers le bas de l'original pour enlever la logique étrangère. Je ne l'ai pas essayé de compiler la version modifiée, de sorte qu'il peut être quelques problèmes mineurs.)

+0

Peut-être qu'avec quelques exemples de code, ce serait plus lisible? – Suma

Questions connexes