Проблема переподключения к названному каналу - PullRequest
6 голосов
/ 05 августа 2011

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

Сервер делает

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

Клиент делает

  1. CreateFile
  2. ReadFile

Порядок выполнения следующий:

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

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

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

Код сервера:

pipe_handle.pipe = CreateNamedPipe(TEXT("\\\\.\\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_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 ]

8 голосов
/ 05 августа 2011

Если вы получили 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;
}
0 голосов
/ 17 ноября 2014

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

1) CloseHandle (Pipe);

2) DisconnectNamedPipe (Pipe);

...