管道客户端不从管道服务器读回答案 (Windows)

Pipe clients don't read back answer from pipe server (Windows)

程序要点:服务器创建n个客户端进程。在客户端用户输入发送到服务器的字符串。在服务器上,字符串处理如下:计算输入字符串中元音和数字出现的频率。此外,此信息被发送到客户端,打印 answer

备注:

  1. 客户是一次性的
  2. 我知道错误:\n 在客户端读取
  3. 服务器的答案输出不应该存在,它的存在只是为了调试

错误要点:服务器成功将应答写入命名管道后,客户端拒绝从管道读取应答。

服务器:

#include <windows.h> 
#include <iostream>
#include <tchar.h>
#include <strsafe.h>
#include <limits>
#include <string.h>
#include <sstream>

#pragma warning(disable : 4996)

using namespace std;

#ifdef max
#undef max
#endif


#define PIPE_TIMEOUT 5000
#define BUFSIZE 4096





int main(int argc, char* argv[])
{
    // ***ROUTINE STAFF***

    cout << "Server is lauched\n"
        "It will be terminated when all clients exits\n";


    if (argc > 2 || (argc == 2 && argv[argc - 1] != "--help")) {
        cout << "Program should be lauched this way:\n"
            "name_of_program --help\n"
            "Note: --help is optional\n";
        return EXIT_FAILURE;
    }
    if (argc == 2 && argv[argc - 1] == "--help") {
        cout << "The server creates n client processes. On the client user\n"
            "enters a character string that is sent to the server.On server\n"
            "the string is processed as follows : the frequency is counted\n"
            "the appearance of vowels and numbers in the entered string.Further,\n"
            "this information is sent to the client.\n";
        return EXIT_SUCCESS;
    }

    int n = 0;
    cout << "Enter number of clients: ";
    if (!(cin >> n) || (n < 0))
    {
        cout << "Invalid input\n";
        return EXIT_FAILURE;
    }
    cin.ignore(numeric_limits<streamsize>::max(), '\n'); // cleaning buffer



    // ***CREATING PROCESSES***

    cout << "Generating " << n << " clients...\n";
    // struct _STARTUPINFO: Specifies the window station, desktop, standard handles, and appearance of the main window for a process at creation time.
    STARTUPINFO* si_arr = new STARTUPINFO[n];
    // struct _PROCESS_INFORMATION: Contains information about a newly created process and its primary thread. It is used with the CreateProcess() (and other).
    PROCESS_INFORMATION* pi_arr = new PROCESS_INFORMATION[n];

    for (int i = 0; i < n; ++i) {
        // ZeroMemory macro: Fills a block of memory with zeros.
        /*
        void ZeroMemory(
        [in] PVOID  Destination,
        [in] SIZE_T Length
        );
        */
        ZeroMemory(&si_arr[i], sizeof(si_arr[i]));
        // DWORD STARTUPINFO.cb: The size of the structure, in bytes.
        si_arr[i].cb = sizeof(si_arr[i]);
        ZeroMemory(&pi_arr[i], sizeof(pi_arr[i]));

        if (!CreateProcess(
        
            TEXT("C:\Users\andre\source\repos\pipe_client\Debug\pipe_client.exe"),   // name of program (like in cmd)
            NULL,        // arguments for program (like in cmd after name of program)
            NULL,           // Process handle not inheritable
            NULL,           // Thread handle not inheritable
            FALSE,          // Set handle inheritance to FALSE
            CREATE_NEW_CONSOLE, // dwCreationFlags - The new process gets a new console instead of inheriting the parent one 
            NULL,           // Use parent's environment block
            NULL,           // Use parent's starting directory 
            &si_arr[i],            // Pointer to STARTUPINFO structure
            &pi_arr[i])           // Pointer to PROCESS_INFORMATION structure
            )
        {
            printf("CreateProcess failed (%d).\n", GetLastError());
            return EXIT_FAILURE;
        }
    }
    cout << "All processes (pipe clients) created\n";


    // ***CREATING PIPE INSTANCES***

    HANDLE* pipe_instances = new HANDLE[n];

    for (int i = 0; i < n; i++)
    {
        pipe_instances[i] = CreateNamedPipe(
            TEXT("\\.\pipe\os_lab4_pipe"),            // pipe name 
            PIPE_ACCESS_DUPLEX,      // read/write access 
            PIPE_TYPE_MESSAGE |      // message-type pipe 
            PIPE_READMODE_MESSAGE |  // message-read mode 
            PIPE_WAIT,               // blocking mode 
            n,               // number of instances 
            1024,   // output buffer size 
            1024,   // input buffer size 
            PIPE_TIMEOUT,            // client time-out 
            NULL);                   // default security attributes 

        if (pipe_instances[i] == INVALID_HANDLE_VALUE)
        {
            printf("CreateNamedPipe failed with %d.\n", GetLastError());
            return EXIT_FAILURE;
        }
    }
    cout << "All pipe instances created\n";

    // ***CONNECTING PIPE INSTANCES***

    for (int i = 0; i < n; i++)
    {
        if (!ConnectNamedPipe(pipe_instances[i], NULL))
        {
            printf("ConnectNamedPipe failed with %d.\n", GetLastError());
            return EXIT_FAILURE;
        }
    }
    cout << "All pipe instances connected to clients\n";

    // ***PROCESSING***

    char buf[1024];
    DWORD read_bytes;
    DWORD written_bytes;

    for (int i = 0; i < n; i++)
    {
        if (ReadFile(pipe_instances[i], buf, 1024, &read_bytes, NULL))
        {
            char* str = new char[read_bytes];
            strncpy(str, buf, read_bytes);
            //char answer[1024]; // ready c-str to WriteFile

            int total_amount = 0;

            int vowel_amount = 0;
            int digit_amount = 0;
            double vowel_frequency = 0.0;
            double digit_frequency = 0.0;


            char vowels[] = "aeiouy";
            char digits[] = "0123456789";


            total_amount = strlen(str);
            printf("Total: %i\n", total_amount);

            // vowel
            char* vowel_search;
            vowel_search = strpbrk(str, vowels); // check for first occurence of vovel in str
            while (vowel_search != NULL) { // while vovels not end up in str
                vowel_amount++;
                vowel_search = strpbrk(vowel_search + 1, vowels);
            }
        
            vowel_frequency = (double)vowel_amount / (double)total_amount * 100.0;
        

            // digit
            char* digit_search;
            digit_search = strpbrk(str, digits); // check for first occurence of digit in str
            while (digit_search != NULL) { // while digits not end up in str
                digit_amount++;
                digit_search = strpbrk(digit_search + 1, digits);
            }
        
            digit_frequency = (double)digit_amount / (double)total_amount * 100.0;
        
            string pre_str;
            pre_str = "Total: " + to_string(total_amount) + "\n"
                "Vowels: " + to_string(vowel_amount) + "\n"
                "Frequency: " + to_string(vowel_frequency) + "\n"
                "Digits: " + to_string(digit_amount) + "\n"
                "Frequency:" + to_string(digit_frequency) + "\n";
        

            cout << pre_str;

            const char* answer = pre_str.c_str();
   
            if (!WriteFile(pipe_instances[i], answer, 1024, &written_bytes, NULL)) {
                printf("WriteFile failed with %d.\n", GetLastError());
                return EXIT_FAILURE;
            }
        }
        else {
            printf("ReadFile failed with %d.\n", GetLastError());
            return EXIT_FAILURE;
        }
    }
    cout << "Reading, processing and writting was successful\n";

    // ***CLOSING PIPE INSTANCES***

    for (int i = 0; i < n; i++)
    {
        CloseHandle(pipe_instances[i]);
    }

    // !? - WaitMultipleObjects()


    delete[] pipe_instances;

    cout << "All pipe instances closed\n";

    // ***CLOSING PROCESSES***
    // The code written below is needed in order for the server to shutdown not earlier than the clients

    HANDLE* ev_hndl_arr = new HANDLE[n];
    for (int i = 0; i < n; i++) {
        ev_hndl_arr[i] = pi_arr[i].hProcess;
    }

    // Wait until EACH child process exits.
    WaitForMultipleObjects(n, ev_hndl_arr, TRUE, INFINITE);

    // Close process and thread handles.
    for (int i = 0; i < n; i++) {
        CloseHandle(pi_arr[i].hProcess);
        CloseHandle(pi_arr[i].hThread);
    }

    delete[] si_arr;
    delete[] pi_arr;
    delete[] ev_hndl_arr;

    cout << "All processes (pipe clients) closed\n";

    cout << "This is the end of server execution\n";
    system("pause");
    return EXIT_SUCCESS;
}

客户:

#include <iostream>
#include <stdio.h> // fgets()
#include <string.h> // strpbrk()

#include <Windows.h>

using namespace std;

int main() {
    cout << "Client is launched\n";

    HANDLE hndlNP = CreateFile(
        TEXT("\\.\pipe\os_lab4_pipe"),
        GENERIC_READ | GENERIC_WRITE,
        NULL, // Prevents other processes from opening a file or device
        NULL, // cannot be inherited by any child processes
        OPEN_EXISTING,
        NULL, // no attributes
        NULL // no template
        );
    if (hndlNP == INVALID_HANDLE_VALUE) {
        cout << "CreateFile error\n";
        return EXIT_FAILURE;
    }
    cout << "Pipe connection established\n";

    char text[1024];


    printf("Enter string (max 1023 symbols): ");
    fgets(text, 1024, stdin);


    char answer[1024];
    DWORD read_bytes;
    DWORD written_bytes;

    if (WriteFile(hndlNP, text, 1024, &written_bytes, NULL)) {
   
        if (ReadFile(hndlNP, answer, 1024, &read_bytes, NULL)) {
            printf("ReadFile failed with %d.\n", GetLastError());
            system("pause"); // TEMPORARY
            return EXIT_FAILURE;
        }
    }
    else {
        printf("WriteFile failed with %d.\n", GetLastError());
        system("pause"); // TEMPORARY
        return EXIT_FAILURE;
    }
    cout << "Writting and reading was successful\n";

    cout << answer;

    // ***CLOSING PIPE CONNECTION***
    CloseHandle(hndlNP);

    system("pause");

    return 0;
}

执行示例:

服务器:

Server is lauched
It will be terminated when all clients exits
Enter number of clients : 2
Generating 2 clients...
===Two clients appears===
All processes(pipe clients) created
All pipe instances created
All pipe instances connected to clients
Total : 8
Total : 8
Vowels : 0
Frequency : 0.000000
Digits : 6
Frequency : 75.000000
Total : 6
Total : 6
Vowels : 0
Frequency : 0.000000
Digits : 5
Frequency : 83.333333
Reading, processingand writting was successful
All pipe instances closed
========================== (waiting until clients exit)
This is the end of server execution
Press any key to continue . . .
(key pressed, server closed)

客户 1:

Client is launched
Pipe connection established
Enter string(max 1023 symbols) : 124345s
ReadFile failed with 0.
Press any key to continue . . .
==========================
(key pressed, client closed)

客户 2:

Client is launched
Pipe connection established
Enter string(max 1023 symbols) : 12234
ReadFile failed with 0.
Press any key to continue . . .
==========================
(key pressed, client closed)

根据 documentation:

If the function succeeds, the return value is nonzero (TRUE).

If the function fails, or is completing asynchronously, the return value is zero (FALSE). To get extended error information, call the GetLastError function.

所以当ReadFile returns非零时,函数执行成功,而你的处理是错误的,你应该修改为:

if (!ReadFile(hndlNP, answer, 1024, &read_bytes, NULL)) {
    printf("ReadFile failed with %d.\n", GetLastError());
    system("pause"); // TEMPORARY
    return EXIT_FAILURE;
}