겹치는 I/O를 사용하는 명명된 파이프 서버

다음은 겹치는 작업을 사용하여 여러 파이프 클라이언트에 대한 동시 연결을 서비스하는 단일 스레드 파이프 서버의 예입니다. 파이프 서버는 고정된 수의 파이프 인스턴스를 만듭니다. 각 파이프 instance 별도의 파이프 클라이언트에 연결할 수 있습니다. 파이프 클라이언트가 파이프 instance 사용을 마치면 서버는 클라이언트에서 연결을 끊고 파이프 instance 다시 사용하여 새 클라이언트에 연결합니다. 이 파이프 서버는 명명된 파이프 클라이언트에 설명된 파이프 클라이언트와 함께 사용할 수 있습니다.

OVERLAPPED 구조체는 파이프 instance 각 ReadFile, WriteFileConnectNamedPipe 작업에서 매개 변수로 지정됩니다. 이 예제에서는 서로 다른 파이프 인스턴스에 대한 동시 작업을 보여 주지만 OVERLAPPED 구조에서 이벤트 개체를 사용하여 단일 파이프 instance 동시 작업을 방지합니다. 각 instance 대한 읽기, 쓰기 및 연결 작업에 동일한 이벤트 개체가 사용되므로 동일한 파이프 instance 사용하여 동시 작업을 위해 이벤트가 신호 상태로 설정되는 작업을 완료했는지 알 수 있는 방법이 없습니다.

각 파이프 instance 대한 이벤트 핸들은 WaitForMultipleObjects 함수에 전달되는 배열에 저장됩니다. 이 함수는 이벤트 중 하나가 신호를 받을 때까지 대기하고 대기 작업이 완료된 이벤트의 배열 인덱스를 반환합니다. 이 항목의 예제에서는 이 배열 인덱스 를 사용하여 파이프 instance 대한 정보가 포함된 구조를 검색합니다. 서버는 구조체의 fPendingIO 멤버를 사용하여 instance 최신 I/O 작업이 보류 중인지 여부를 추적합니다. 이 경우 GetOverlappedResult 함수를 호출해야 합니다. 서버는 구조체의 dwState 멤버를 사용하여 파이프 instance 대해 수행해야 하는 다음 작업을 결정합니다.

겹치는 ReadFile, WriteFileConnectNamedPipe 작업은 함수가 반환될 때까지 완료할 수 있습니다. 그렇지 않으면 작업이 보류 중인 경우 지정된 OVERLAPPED 구조체의 이벤트 개체가 함수가 반환되기 전에 부호 없는 상태로 설정됩니다. 보류 중인 작업이 완료되면 시스템은 이벤트 개체의 상태를 신호로 설정합니다. 함수가 반환되기 전에 작업이 완료되면 이벤트 개체의 상태가 변경되지 않습니다.

이 예제에서는 수동 재설정 이벤트 개체를 사용하므로 WaitForMultipleObjects 함수에 의해 이벤트 개체의 상태가 서명되지 않음으로 변경되지 않습니다. 이 예제는 보류 중인 작업이 있는 경우를 제외하고 신호 상태가 남아 있는 이벤트 개체를 사용하므로 중요합니다.

ReadFile, WriteFile 또는 ConnectNamedPipe가 반환될 때 작업이 이미 완료된 경우 함수의 반환 값은 결과를 나타냅니다. 읽기 및 쓰기 작업의 경우 전송된 바이트 수도 반환됩니다. 작업이 아직 보류 중인 경우 ReadFile, WriteFile 또는 ConnectNamedPipe 함수는 0을 반환하고 GetLastError 함수는 ERROR_IO_PENDING 반환합니다. 이 경우 GetOverlappedResult 함수를 사용하여 작업이 완료된 후 결과를 검색합니다. GetOverlappedResult 는 보류 중인 작업의 결과만 반환합니다. 겹치는 ReadFile, WriteFile 또는 ConnectNamedPipe 함수가 반환되기 전에 완료된 작업의 결과를 보고하지 않습니다.

클라이언트에서 연결을 끊기 전에 클라이언트가 완료되었음을 나타내는 신호를 기다려야 합니다. (플러시 작업은 클라이언트가 파이프를 비울 때까지 기다리는 동안 서버 스레드의 실행을 차단하기 때문에 파일 버퍼를 플러시하면 겹치는 I/O의 목적이 무효화됩니다.) 이 예제에서 신호는 파이프 클라이언트가 핸들을 닫은 후 파이프에서 읽으려고 시도하여 생성된 오류입니다.

#include <windows.h> 
#include <stdio.h>
#include <tchar.h>
#include <strsafe.h>
 
#define CONNECTING_STATE 0 
#define READING_STATE 1 
#define WRITING_STATE 2 
#define INSTANCES 4 
#define PIPE_TIMEOUT 5000
#define BUFSIZE 4096
 
typedef struct 
{ 
   OVERLAPPED oOverlap; 
   HANDLE hPipeInst; 
   TCHAR chRequest[BUFSIZE]; 
   DWORD cbRead;
   TCHAR chReply[BUFSIZE];
   DWORD cbToWrite; 
   DWORD dwState; 
   BOOL fPendingIO; 
} PIPEINST, *LPPIPEINST; 
 
 
VOID DisconnectAndReconnect(DWORD); 
BOOL ConnectToNewClient(HANDLE, LPOVERLAPPED); 
VOID GetAnswerToRequest(LPPIPEINST); 
 
PIPEINST Pipe[INSTANCES]; 
HANDLE hEvents[INSTANCES]; 
 
int _tmain(VOID) 
{ 
   DWORD i, dwWait, cbRet, dwErr; 
   BOOL fSuccess; 
   LPCTSTR lpszPipename = TEXT("\\\\.\\pipe\\mynamedpipe"); 
 
// The initial loop creates several instances of a named pipe 
// along with an event object for each instance.  An 
// overlapped ConnectNamedPipe operation is started for 
// each instance. 
 
   for (i = 0; i < INSTANCES; i++) 
   { 
 
   // Create an event object for this instance. 
 
      hEvents[i] = CreateEvent( 
         NULL,    // default security attribute 
         TRUE,    // manual-reset event 
         TRUE,    // initial state = signaled 
         NULL);   // unnamed event object 

      if (hEvents[i] == NULL) 
      {
         printf("CreateEvent failed with %d.\n", GetLastError()); 
         return 0;
      }
 
      Pipe[i].oOverlap.hEvent = hEvents[i]; 
      Pipe[i].oOverlap.Offset = 0;
      Pipe[i].oOverlap.OffsetHigh = 0;
 
      Pipe[i].hPipeInst = CreateNamedPipe( 
         lpszPipename,            // pipe name 
         PIPE_ACCESS_DUPLEX |     // read/write access 
         FILE_FLAG_OVERLAPPED,    // overlapped mode 
         PIPE_TYPE_MESSAGE |      // message-type pipe 
         PIPE_READMODE_MESSAGE |  // message-read mode 
         PIPE_WAIT,               // blocking mode 
         INSTANCES,               // number of instances 
         BUFSIZE*sizeof(TCHAR),   // output buffer size 
         BUFSIZE*sizeof(TCHAR),   // input buffer size 
         PIPE_TIMEOUT,            // client time-out 
         NULL);                   // default security attributes 

      if (Pipe[i].hPipeInst == INVALID_HANDLE_VALUE) 
      {
         printf("CreateNamedPipe failed with %d.\n", GetLastError());
         return 0;
      }
 
   // Call the subroutine to connect to the new client
 
      Pipe[i].fPendingIO = ConnectToNewClient( 
         Pipe[i].hPipeInst, 
         &Pipe[i].oOverlap); 
 
      Pipe[i].dwState = Pipe[i].fPendingIO ? 
         CONNECTING_STATE : // still connecting 
         READING_STATE;     // ready to read 
   } 
 
   while (1) 
   { 
   // Wait for the event object to be signaled, indicating 
   // completion of an overlapped read, write, or 
   // connect operation. 
 
      dwWait = WaitForMultipleObjects( 
         INSTANCES,    // number of event objects 
         hEvents,      // array of event objects 
         FALSE,        // does not wait for all 
         INFINITE);    // waits indefinitely 
 
   // dwWait shows which pipe completed the operation. 
 
      i = dwWait - WAIT_OBJECT_0;  // determines which pipe 
      if (i < 0 || i > (INSTANCES - 1)) 
      {
         printf("Index out of range.\n"); 
         return 0;
      }
 
   // Get the result if the operation was pending. 
 
      if (Pipe[i].fPendingIO) 
      { 
         fSuccess = GetOverlappedResult( 
            Pipe[i].hPipeInst, // handle to pipe 
            &Pipe[i].oOverlap, // OVERLAPPED structure 
            &cbRet,            // bytes transferred 
            FALSE);            // do not wait 
 
         switch (Pipe[i].dwState) 
         { 
         // Pending connect operation 
            case CONNECTING_STATE: 
               if (! fSuccess) 
               {
                   printf("Error %d.\n", GetLastError()); 
                   return 0;
               }
               Pipe[i].dwState = READING_STATE; 
               break; 
 
         // Pending read operation 
            case READING_STATE: 
               if (! fSuccess || cbRet == 0) 
               { 
                  DisconnectAndReconnect(i); 
                  continue; 
               }
               Pipe[i].cbRead = cbRet;
               Pipe[i].dwState = WRITING_STATE; 
               break; 
 
         // Pending write operation 
            case WRITING_STATE: 
               if (! fSuccess || cbRet != Pipe[i].cbToWrite) 
               { 
                  DisconnectAndReconnect(i); 
                  continue; 
               } 
               Pipe[i].dwState = READING_STATE; 
               break; 
 
            default: 
            {
               printf("Invalid pipe state.\n"); 
               return 0;
            }
         }  
      } 
 
   // The pipe state determines which operation to do next. 
 
      switch (Pipe[i].dwState) 
      { 
      // READING_STATE: 
      // The pipe instance is connected to the client 
      // and is ready to read a request from the client. 
 
         case READING_STATE: 
            fSuccess = ReadFile( 
               Pipe[i].hPipeInst, 
               Pipe[i].chRequest, 
               BUFSIZE*sizeof(TCHAR), 
               &Pipe[i].cbRead, 
               &Pipe[i].oOverlap); 
 
         // The read operation completed successfully. 
 
            if (fSuccess && Pipe[i].cbRead != 0) 
            { 
               Pipe[i].fPendingIO = FALSE; 
               Pipe[i].dwState = WRITING_STATE; 
               continue; 
            } 
 
         // The read operation is still pending. 
 
            dwErr = GetLastError(); 
            if (! fSuccess && (dwErr == ERROR_IO_PENDING)) 
            { 
               Pipe[i].fPendingIO = TRUE; 
               continue; 
            } 
 
         // An error occurred; disconnect from the client. 
 
            DisconnectAndReconnect(i); 
            break; 
 
      // WRITING_STATE: 
      // The request was successfully read from the client. 
      // Get the reply data and write it to the client. 
 
         case WRITING_STATE: 
            GetAnswerToRequest(&Pipe[i]); 
 
            fSuccess = WriteFile( 
               Pipe[i].hPipeInst, 
               Pipe[i].chReply, 
               Pipe[i].cbToWrite, 
               &cbRet, 
               &Pipe[i].oOverlap); 
 
         // The write operation completed successfully. 
 
            if (fSuccess && cbRet == Pipe[i].cbToWrite) 
            { 
               Pipe[i].fPendingIO = FALSE; 
               Pipe[i].dwState = READING_STATE; 
               continue; 
            } 
 
         // The write operation is still pending. 
 
            dwErr = GetLastError(); 
            if (! fSuccess && (dwErr == ERROR_IO_PENDING)) 
            { 
               Pipe[i].fPendingIO = TRUE; 
               continue; 
            } 
 
         // An error occurred; disconnect from the client. 
 
            DisconnectAndReconnect(i); 
            break; 
 
         default: 
         {
            printf("Invalid pipe state.\n"); 
            return 0;
         }
      } 
  } 
 
  return 0; 
} 
 
 
// DisconnectAndReconnect(DWORD) 
// This function is called when an error occurs or when the client 
// closes its handle to the pipe. Disconnect from this client, then 
// call ConnectNamedPipe to wait for another client to connect. 
 
VOID DisconnectAndReconnect(DWORD i) 
{ 
// Disconnect the pipe instance. 
 
   if (! DisconnectNamedPipe(Pipe[i].hPipeInst) ) 
   {
      printf("DisconnectNamedPipe failed with %d.\n", GetLastError());
   }
 
// Call a subroutine to connect to the new client. 
 
   Pipe[i].fPendingIO = ConnectToNewClient( 
      Pipe[i].hPipeInst, 
      &Pipe[i].oOverlap); 
 
   Pipe[i].dwState = Pipe[i].fPendingIO ? 
      CONNECTING_STATE : // still connecting 
      READING_STATE;     // ready to read 
} 
 
// ConnectToNewClient(HANDLE, LPOVERLAPPED) 
// This function is called to start an overlapped connect operation. 
// It returns TRUE if an operation is pending or FALSE if the 
// connection has been completed. 
 
BOOL ConnectToNewClient(HANDLE hPipe, LPOVERLAPPED lpo) 
{ 
   BOOL fConnected, fPendingIO = FALSE; 
 
// Start an overlapped connection for this pipe instance. 
   fConnected = ConnectNamedPipe(hPipe, lpo); 
 
// Overlapped ConnectNamedPipe should return zero. 
   if (fConnected) 
   {
      printf("ConnectNamedPipe failed with %d.\n", GetLastError()); 
      return 0;
   }
 
   switch (GetLastError()) 
   { 
   // The overlapped connection in progress. 
      case ERROR_IO_PENDING: 
         fPendingIO = TRUE; 
         break; 
 
   // Client is already connected, so signal an event. 
 
      case ERROR_PIPE_CONNECTED: 
         if (SetEvent(lpo->hEvent)) 
            break; 
 
   // If an error occurs during the connect operation... 
      default: 
      {
         printf("ConnectNamedPipe failed with %d.\n", GetLastError());
         return 0;
      }
   } 
 
   return fPendingIO; 
}

VOID GetAnswerToRequest(LPPIPEINST pipe)
{
   _tprintf( TEXT("[%d] %s\n"), pipe->hPipeInst, pipe->chRequest);
   StringCchCopy( pipe->chReply, BUFSIZE, TEXT("Default answer from server") );
   pipe->cbToWrite = (lstrlen(pipe->chReply)+1)*sizeof(TCHAR);
}

명명된 파이프 클라이언트