Проблема повторного подключения к именованному каналу

у меня есть сервер именованного канала и клиент. (Делая это в VC++).

сервер тут

  1. CreateNamedPipe
  2. ConnectNamedPipe
  3. WriteFile
  4. отключить
  5. повторите от 2 до 4

клиент тут

  1. CreateFile
  2. функцию ReadFile

порядок исполнения в следует,

  1. Сервер -- CreateNamedPipe
  2. Клиент -- CreateFile
  3. Server -- ConnectNamedPipe (должен немедленно вернуться, поскольку клиент уже подключен)
  4. Сервер -- WriteFile
  5. Клиент -- ReadFile
  6. Сервер -- DisconnectNamedPipe
  7. Клиент -- CloseHandle
  8. Гото 2

это отлично работает в первый раз. Однако проблема возникает, когда клиент пытается подключается во второй раз. Когда клиент пытается подключиться (CreateFile) во второй раз до сервер сделал ConnectNamedPipe (но после disconnectnamedpipe), он получает ERROR_PIPE_BUSY. Он работает, если клиент вызывает createfile после того, как сервер вызывает ConnectNamedPipe.

есть ли в любом случае, что я могу подключиться к клиенту (CreateFile) перед сервером ConnectNamedPipe (после DisconnectNamedPipe)?

сервер код:

pipe_handle.pipe = CreateNamedPipe(TEXT("\.pipetestpipe1"),
                PIPE_ACCESS_OUTBOUND |
                FILE_FLAG_OVERLAPPED,        // read/write access
                PIPE_TYPE_MESSAGE |           // message type pipe
                PIPE_READMODE_MESSAGE |       // message-read mode
                PIPE_WAIT,                    // blocking mode
                PIPE_UNLIMITED_INSTANCES,     // max. instances
                BUFFER_SIZE,                  // output buffer size
                BUFFER_SIZE,                  // input buffer size
                2000,              // client time-out
                NULL);

if (pipe_handle.pipe == INVALID_HANDLE_VALUE) {
    std::cout << "Error while creating pipe" << std::endl;
    return -1;
}
std::cout <<"Connecting to named pipe" << std::endl;

std::cout<< "Somebody connected to named pipe" << std::endl;

int ac;

for (ac=0; ac<2; ac++) {

    char a[25];
    // Wait for some input. This helps me to start the client in other terminal.
    cin >> a;
    cout << "Connecting..." << endl;

    ConnectNamedPipe(pipe_handle.pipe, 0);

    cout << "Connect pipe returned." << endl;

    // Wait for some input.
    cin >> a;
    string message = "Test message";
    DWORD bytes_written;

    if (!WriteFile(pipe_handle.pipe, message.c_str(), message.size(),
                   &bytes_written, NULL)) {

        DWORD er = GetLastError();
        char errs[200];
        sprintf(errs, "Error : %ld", er);
        std::cout << "Error communicating to client.";
        std::cout << errs;
    }
    std::cout << "Written to pipe";
    FlushFileBuffers(pipe_handle.pipe);
    if (!DisconnectNamedPipe(pipe_handle.pipe)) {
        std::cout << "Disconnect failed"<< GetLastError() << endl;
    } else {
        std::cout << "Disconnect successful"<<endl;
    }
}

Клиентский Код:

while (1) { 

    std::cout << "Returned" << std::endl;
    hPipe = CreateFile( 
              lpszPipename,   // pipe name 
              GENERIC_READ, 
              0,              // no sharing 
              NULL,           // default security attributes
              OPEN_EXISTING,  // opens existing pipe 
              FILE_FLAG_OVERLAPPED,              // default attributes 
              NULL);          // no template file 

    // Break if the pipe handle is valid. 

    if (hPipe != INVALID_HANDLE_VALUE) 
        break; 


    // Exit if an error other than ERROR_PIPE_BUSY occurs. 

    if (GetLastError() != ERROR_PIPE_BUSY) {
        std::cout<< "Could not open pipe " << GetLastError() << std::endl; 
        return -1;
    }

    // All pipe instances are busy, so wait for sometime.

    if ( ! WaitNamedPipe(lpszPipename, NMPWAIT_USE_DEFAULT_WAIT)) { 
        std::cout<<  "Could not open pipe: wait timed out." << std::endl; 
    } 
}

OVERLAPPED ol1;

memset(&ol1, 0, sizeof(ol1));
ol1.Offset = 0;
ol1.OffsetHigh = 0;
ol1.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

HANDLE events[1];
events[0] = ol1.hEvent;
cbToWrite = (lstrlen(message)+1)*sizeof(TCHAR);

DWORD bytes_to_read = 2000;
char * buf = reinterpret_cast<char *>(malloc(bytes_to_read));
DWORD bytes_read;

std::cout << "Waiting for read" << std::endl;
bool a = ReadFile(hPipe, buf, bytes_to_read, &bytes_read, &ol1);


if ( ! fSuccess) {
    std::cout << "WriteFile to pipe failed. GLE " << GetLastError() << std::endl; 
}
std::cout << "Waiting for multiple objects" << std::endl;
WaitForMultipleObjects(1, events, FALSE, INFINITE);
std::cout << "multiple objects returned" << std::endl;
printf("nMessage sent to server");
CancelIo(hPipe);
CloseHandle(hPipe);

2 ответов


Если вы получаете ERROR_PIPE_BUSY на вызове CreateFile() в клиенте, вам нужно вызвать WaitNamedPipe (), а затем повторить попытку, когда он вернется. Если вы получаете возврат нуля из WaitNamedPipe (), это означает, что он истекает без того, чтобы труба стала доступной. Вы никогда не увидите, что произойдет, если вы передадите nmpwait_wait_forever в качестве таймаута.

вам также нужно иметь в виду, что труба может снова стать занятой между возвращением WaitNamedPipe() и вызовом CreateFile (); поэтому вы нужно сделать это в цикле. Вот так:

while (true)
{
    hPipe = CreateFile(pipeName,
                       GENERIC_READ | GENERIC_WRITE,
                       0,
                       0,
                       OPEN_EXISTING,
                       FILE_ATTRIBUTE_NORMAL,
                       0);
    if (hPipe == INVALID_HANDLE_VALUE)
    {
        if (GetLastError() == ERROR_PIPE_BUSY)
        {
            if (!WaitNamedPipe(pipeName, NMPWAIT_USE_DEFAULT_WAIT))
                continue;   // timeout, try again
        }
        else
            return false;   // error
    }
    else
        break;   // success
}

EDIT:

я упростил свой код и теперь он работает нормально. Работы сервера и клиента.

сервер:

#include <windows.h>
#include <stdio.h>

int main(void)
{
    HANDLE pipe;
    const DWORD BUFFER_SIZE = 1024;

    pipe = CreateNamedPipe("\\.\pipe\testpipe1",
                                  PIPE_ACCESS_OUTBOUND |
                                  FILE_FLAG_OVERLAPPED,          // read/write access
                                  PIPE_TYPE_MESSAGE |             // message type pipe
                                  PIPE_READMODE_MESSAGE |         // message-read mode
                                  PIPE_WAIT,                          // blocking mode
                                  PIPE_UNLIMITED_INSTANCES,   // max. instances
                                  BUFFER_SIZE,                        // output buffer size
                                  BUFFER_SIZE,                        // input buffer size
                                  2000,                 // client time-out
                                  NULL);

    if (pipe == INVALID_HANDLE_VALUE)
    {
        printf("Error while creating pipe\n");
        return -1;
    }
    printf("Connecting to named pipe\n");

    int ac;

    for (ac=0; ac<2; ac++)
    {
        // Wait for some input. This helps me to start the client in other terminal.
        printf("Connecting...\n");

        ConnectNamedPipe(pipe, 0);

        printf("Connect pipe returned.\n");

        // Wait for some input.
        char * message = "Test message";
        DWORD bytes_written;

        if (!WriteFile(pipe, message, strlen(message)+1, &bytes_written, NULL))
        {

            DWORD er = GetLastError();
            char errs[200];
            sprintf_s(errs, "Error : %ld", er);
            printf("Error communicating to client.\n");
            printf(errs);
        }
        printf("Written to pipe\n");
        FlushFileBuffers(pipe);
        if (!DisconnectNamedPipe(pipe))
        {
            printf("Disconnect failed %d\n", GetLastError());
        }
        else
        {
            printf("Disconnect successful\n");
        }
    }
}

клиент:

#include <windows.h>
#include <stdio.h>

int main(void)
{
    HANDLE hPipe;

    while (1)
    {

        printf("Returned\n");
        hPipe = CreateFile("\\.\pipe\testpipe1",
                                GENERIC_READ, 
                                0,                   // no sharing 
                                NULL,                // default security attributes
                                OPEN_EXISTING,   // opens existing pipe 
                                0,                // default attributes 
                                NULL);           // no template file 

        // Break if the pipe handle is valid. 

        if (hPipe != INVALID_HANDLE_VALUE)
            break;


        // Exit if an error other than ERROR_PIPE_BUSY occurs. 

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

        // All pipe instances are busy, so wait for sometime.

        if ( ! WaitNamedPipe("\\.\pipe\testpipe1", NMPWAIT_USE_DEFAULT_WAIT))
        {
            printf("Could not open pipe: wait timed out.\n"); 
        }
    }


    char *message = "hello";
    DWORD cbToWrite = (strlen(message)+1)*sizeof(message[0]);

    DWORD bytes_to_read = 2000;
    char * buf = reinterpret_cast<char *>(malloc(bytes_to_read));
    DWORD bytes_read;

    printf("Waiting for read\n");
    bytes_read = 0;
    ReadFile(hPipe, buf, bytes_to_read, &bytes_read, 0);

    if (bytes_read <= 0)
    {
        printf("ReadFile from pipe failed. GLE \n"); 
    }
    else
        printf("Read %d bytes: %s\n", bytes_read, buf);

    CloseHandle(hPipe);
    return 0;
}

на стороне сервера, когда вы решите разорвать соединение, вы должны использовать цепочку:

1) CloseHandle (Труба);

2) DisconnectNamedPipe (Труба);