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

时间:2021-03-31 04:55:18

标签: windows winapi ipc named-pipes duplex

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

备注:

  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)

1 个答案:

答案 0 :(得分:0)

根据documentation

<块引用>

如果函数成功,则返回值非零 (TRUE)。

如果函数失败,或者正在异步完成,则返回 值为零 ()。要获取扩展错误信息,请调用 GetLastError 函数。

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

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