Несколько WriteFile после ERROR_IO_PENDING

0

В моем приложении я тестирую поведение WriteFile, когда другая иная операция выполняется над именованным каналом.

Труба находится в режиме сообщения (не в байтовом режиме).

Чтобы сделать операцию записи ожидающей, я сохранил буфер именованного канала совсем небольшим, и клиент отправил больше данных, чем размер буфера. Таким образом, я получаю операцию записи в ожидании на стороне клиента.

На сервере я столкнулся со следующей проблемой:

ReadFile с ERROR_MORE_DATA. Он изменяет содержимое буфера, но не количество полученных байтов.

Код сервера

//file server.cpp
#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 3
#define BUFSIZE2 1000

typedef struct 
{ 
   OVERLAPPED oOverlap; 
   HANDLE hPipeInst; 
   TCHAR chRequest[BUFSIZE2]; 
   DWORD cbRead;
   DWORD cbReadSoFar;
   TCHAR chReply[BUFSIZE2];
   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; 
   LPTSTR lpszPipename = TEXT("\\\\.\\pipe\\mynamedpipe"); 

   for (i = 0; i < INSTANCES; i++) 
   { 
      hEvents[i] = CreateEvent(NULL, TRUE, TRUE, NULL);

      Pipe[i].oOverlap.hEvent = hEvents[i]; 
      Pipe[i].hPipeInst = CreateNamedPipe(lpszPipename, PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED,  
         PIPE_TYPE_MESSAGE|PIPE_READMODE_MESSAGE|PIPE_WAIT, INSTANCES,                
         BUFSIZE*sizeof(TCHAR), BUFSIZE*sizeof(TCHAR), PIPE_TIMEOUT, NULL);                   

      if (Pipe[i].hPipeInst == INVALID_HANDLE_VALUE) 
      {
         printf("CreateNamedPipe failed with %d.\n", GetLastError());
         return 0;
      }

      Pipe[i].fPendingIO = ConnectToNewClient(Pipe[i].hPipeInst, &Pipe[i].oOverlap); 
      Pipe[i].dwState = Pipe[i].fPendingIO ? CONNECTING_STATE : READING_STATE;
   } 

   while (1) 
   {
      dwWait = WaitForMultipleObjects(INSTANCES, hEvents, FALSE, INFINITE);

      i = dwWait - WAIT_OBJECT_0;  // determines which pipe 
      if (i < 0 || i > (INSTANCES - 1)) 
      {
         printf("Index out of range.\n"); 
         return 0;
      }

      if (Pipe[i].fPendingIO) 
      { 
         fSuccess = GetOverlappedResult(Pipe[i].hPipeInst, &Pipe[i].oOverlap, &cbRet, FALSE);

         DWORD lasterror = GetLastError();
         switch (Pipe[i].dwState) 
         { 
            case CONNECTING_STATE: 
               if (! fSuccess) 
               {
                   printf("Error %d.\n", GetLastError()); 
                   return 0;
               }
               Pipe[i].dwState = READING_STATE; 
               Pipe[i].cbReadSoFar = 0;
               break; 

            case READING_STATE: 
                if(ERROR_MORE_DATA == lasterror)
                {
                    Pipe[i].cbReadSoFar += cbRet;
                    break;
                }
                if(ERROR_IO_PENDING == lasterror)
                {
                    continue;
                }
                if (! fSuccess || cbRet == 0) 
                { 
                    DisconnectAndReconnect(i); 
                    continue; 
                }
                Pipe[i].cbRead = cbRet;
                Pipe[i].cbReadSoFar = 0;
                printf("Message received: %s\n", Pipe[i].chRequest);
                break;

            default: 
            {
               printf("Invalid pipe state.\n"); 
               return 0;
            }
         }  
      } 

      switch (Pipe[i].dwState) 
      { 
         case READING_STATE: 
            fSuccess = ReadFile( 
               Pipe[i].hPipeInst, 
               Pipe[i].chRequest + Pipe[i].cbReadSoFar, 
               BUFSIZE*sizeof(TCHAR), 
               &Pipe[i].cbRead, 
               &Pipe[i].oOverlap); 

            dwErr = GetLastError(); 
            if (! fSuccess && (dwErr == ERROR_IO_PENDING)) 
            { 
               Pipe[i].fPendingIO = TRUE; 
               continue; 
            } 

            if(!fSuccess && dwErr == ERROR_MORE_DATA)
            {
                Pipe[i].cbReadSoFar += Pipe[i].cbRead;
                continue;
            }
            if (fSuccess && Pipe[i].cbRead != 0) 
            { 
               Pipe[i].fPendingIO = FALSE;
               Pipe[i].cbReadSoFar = 0;
               //Log the message.
                printf("Message received: %s\n", Pipe[i].chRequest);
               continue; 
            }
         // An error occurred; disconnect from the client. 

            DisconnectAndReconnect(i); 
            break; 

         default: 
         {
            printf("Invalid pipe state.\n"); 
            return 0;
         }
      } 
  } 

  return 0; 
} 

VOID DisconnectAndReconnect(DWORD i) 
{ 
   if (! DisconnectNamedPipe(Pipe[i].hPipeInst) ) 
   {
      printf("DisconnectNamedPipe failed with %d.\n", GetLastError());
   }

   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 
} 

BOOL ConnectToNewClient(HANDLE hPipe, LPOVERLAPPED lpo) 
{ 
   BOOL fConnected, fPendingIO = FALSE; 
   fConnected = ConnectNamedPipe(hPipe, lpo); 
   if (fConnected) 
   {
      printf("ConnectNamedPipe failed with %d.\n", GetLastError()); 
      return 0;
   }

   switch (GetLastError()) 
   { 
      case ERROR_IO_PENDING: 
         fPendingIO = TRUE; 
         break; 

      case ERROR_PIPE_CONNECTED: 
         if (SetEvent(lpo->hEvent)) 
            break; 

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

И клиентский код

//file: client.cpp
#include <windows.h> 
#include <stdio.h>
#include <conio.h>
#include <tchar.h>

#define BUFSIZE 512
#define NUM_MESSAGE 4
int _tmain(int argc, TCHAR *argv[]) 
{ 
   HANDLE hPipe; 
   LPTSTR lpvMessage=TEXT("Default message from the client."); 
   TCHAR  chBuf[NUM_MESSAGE+1][BUFSIZE]; 
   BOOL   fSuccess = FALSE; 
   DWORD  cbRead, cbToWrite, cbWritten, dwMode; 
   LPTSTR lpszPipename = TEXT("\\\\.\\pipe\\mynamedpipe"); 

   while (1) 
   { 
      hPipe = CreateFile(lpszPipename, GENERIC_READ|GENERIC_WRITE, 
         0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);

      if (hPipe != INVALID_HANDLE_VALUE) 
         break; 

      if (GetLastError() != ERROR_PIPE_BUSY) 
      {
         _tprintf( TEXT("Could not open pipe. GLE=%d\n"), GetLastError() ); 
         return -1;
      }

      if ( ! WaitNamedPipe(lpszPipename, 20000)) 
      { 
         printf("Could not open pipe: 20 second wait timed out."); 
         return -1;
      } 
   } 

   dwMode = PIPE_READMODE_MESSAGE; 
   fSuccess = SetNamedPipeHandleState(hPipe, &dwMode, NULL, NULL);
   if (!fSuccess) 
   {
    _tprintf(TEXT("SetNamedPipeHandleState failed. GLE=%d\n"), GetLastError()); 
    return -1;
   }


   cbToWrite = (lstrlen(lpvMessage)+1)*sizeof(TCHAR);
   _tprintf( TEXT("Sending %d byte message: \"%s\"\n"), cbToWrite, lpvMessage); 

   OVERLAPPED woverlapped[NUM_MESSAGE];
   HANDLE   wevent[NUM_MESSAGE];
   memset(woverlapped, 0, sizeof(woverlapped));
   for(int i=0; i<NUM_MESSAGE; ++i)
   {
    wevent[i]   = CreateEvent(NULL, 0, 0, NULL);
    woverlapped[i].hEvent = wevent[i];
   }

   const int retrycount = NUM_MESSAGE;
   int numberofsend = retrycount;   

wretry:   
   cbToWrite = _stprintf(chBuf[retrycount-numberofsend], TEXT("Message %d from Client"), retrycount - numberofsend + 1);
   cbToWrite *= sizeof(TCHAR);

   fSuccess = WriteFile(hPipe, chBuf[retrycount-numberofsend], cbToWrite,
      &cbWritten, &woverlapped[retrycount-numberofsend]);

    --numberofsend;
   if(!fSuccess) 
   {
    _tprintf( TEXT("WriteFile to pipe failed. GLE=%d\n"), GetLastError() ); 
    //return -1;
    if(GetLastError() != ERROR_IO_PENDING)
        return -1;  
   }

    if(numberofsend)
        goto wretry;

    bool wait = true;
    while(wait)
    {
        DWORD retval = WaitForMultipleObjects(NUM_MESSAGE, wevent, FALSE, INFINITE);

        retval -= WAIT_OBJECT_0;
        DWORD cbret;
        DWORD success = ::GetOverlappedResult(hPipe, &woverlapped[retval], &cbret, FALSE);
        DWORD laserr = ::GetLastError();
        ++numberofsend;

        if(numberofsend == retrycount)
            wait = false;
    }
   CloseHandle(hPipe); 

   return 0; 
}

В таком случае я делаю что-то не так на сервере? Или это ожидается?

Теги:
io
winapi
named-pipes

1 ответ

1
Лучший ответ

Чтобы процитировать MSDN:

Если именованный канал читается в режиме сообщения, а следующее сообщение длиннее параметра nNumberOfBytesToRead, ReadFile возвращает FALSE, а GetLastError возвращает ERROR_MORE_DATA. Остальная часть сообщения может быть прочитана последующим вызовом функции ReadFile или PeekNamedPipe.

  • 0
    Спасибо за ответ. Я только что понял это. Я увеличил nNumberOfBytesToRead, и он отлично работает.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню