Winapi 客户端与服务器的独立并行工作

Winapi 客户端与服务器的独立并行工作,winapi,parallel-processing,ipc,named-pipes,multiprocess,Winapi,Parallel Processing,Ipc,Named Pipes,Multiprocess,程序要点:服务器创建n个客户端进程。在客户端上,用户输入发送到服务器的字符串。在服务器上,字符串的处理方式如下:频率根据输入字符串中元音和数字的出现次数计算。此外,该信息被发送到客户机,客户机打印答案 备注: 客户是一次性的 服务器的应答输出不应存在,它的存在仅用于调试 bug的要点:要在服务器上获得即时处理并将响应从服务器即时返回到客户机,您需要按照客户机创建的顺序处理客户机。但是,如果我们在除第一个客户机之外的所有客户机中输入一行,那么我们将不会看到来自服务器端的任何操作(它不会打印任何内容

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

备注:

  • 客户是一次性的
  • 服务器的应答输出不应存在,它的存在仅用于调试
  • bug的要点:要在服务器上获得即时处理并将响应从服务器即时返回到客户机,您需要按照客户机创建的顺序处理客户机。但是,如果我们在除第一个客户机之外的所有客户机中输入一行,那么我们将不会看到来自服务器端的任何操作(它不会打印任何内容)。但是,一旦您在第一个客户机中输入一行,来自所有客户机的信息将立即被处理,并且处理将按照创建客户机的顺序进行(根据服务器的调试输出)

    问题:如何实现每个客户端独立于其创建队列

    服务器:

    #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 occurence 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\\andrewshnitko\\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;
    }
    
    客户端(第二个)://第一个输入到第二个客户端

    Client is launched
    Pipe connection established
    Enter string (max 1023 symbols): 12345qwerty
    ***BREAKPOINT 1: no answer from server, no debug output in server side (waiting state)***
    (waiting)
    ***BREAKPOINT 2: immediate response from server and debug output from server(firstly about
    first client(wrote for the second time), and then about second client(wrote for the first 
    time)). All clients get answer from server***
    Writting and reading was successful
    Total: 11
    Vowels: 2
    Frequency: 18.181818
    Digits: 5
    Frequency:45.454545
    Press any key to continue . . .
    (press)
    (termination)
    
    客户(第一名):


    注意:首先创建客户端(第一个),然后创建客户端(第二个)

    您可以通过多个线程检测不同的管道,您可以参考以下代码:

    Server.cpp

    #include <windows.h> 
    #include <iostream>
    #include <tchar.h>
    #include <strsafe.h>
    #include <limits>
    #include <string.h>
    #include <sstream>
    #include <thread>
    
    #pragma warning(disable : 4996)
    
    using namespace std;
    
    #ifdef max
    #undef max
    #endif
    
    
    #define PIPE_TIMEOUT 5000
    #define BUFSIZE 4096
    HANDLE* pipe_instances = NULL;
    int n = 0;
    
    
    int f(int i)
    {
        char buf[1024];
        DWORD read_bytes;
        DWORD written_bytes;
        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;
        }
    }
    
    
    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 occurence of vowels and numbers in the entered string. Further,\n"
        //        "this information is sent to the client.\n";
        //    return EXIT_SUCCESS;
        //}
    
        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("D:\\VS_test_projects\\Project2\\Debug\\c.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***
    
        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***
    
        thread* t = new thread[n];
        for (int i = 0; i < n; i++)
        {
            t[i] = thread(f, i);
        }
    
    
        cout << "Reading, processing and writting was successful\n";
    
        // ***CLOSING PIPE INSTANCES***
        for (int i = 0; i < n; i++)
        {
            t[i].join();
        }
        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;
    }
    
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    #杂注警告(禁用:4996)
    使用名称空间std;
    #ifdef最大值
    #未定义最大值
    #恩迪夫
    #定义管道超时5000
    #定义BUFSIZE 4096
    HANDLE*pipe_实例=NULL;
    int n=0;
    int f(int i)
    {
    char-buf[1024];
    DWORD读取字节;
    DWORD写入字节;
    if(ReadFile(管道实例[i],buf,1024,&读取字节,NULL))
    {
    char*str=新字符[读取字节];
    strncpy(str、buf、read_字节);
    //char-answer[1024];//准备好c-str写入文件了吗
    int总金额=0;
    int元音_数量=0;
    整数位数=0;
    双元音_频率=0.0;
    两位数_频率=0.0;
    字符元音[]=“aeiouy”;
    字符数字[]=“0123456789”;
    总金额=strlen(str);
    printf(“总计:%i\n”,总金额);
    //元音
    字符*元音搜索;
    元音搜索=strpbrk(str,元音);//检查str中是否首次出现vovel
    while(元音搜索!=NULL){//while vovels不以str结尾
    元音_数量++;
    元音搜索=strpbrk(元音搜索+1,元音);
    }
    元音字母频率=(双)元音字母字母数量/(双)总字母数量*100.0;
    //数字
    字符*数字搜索;
    digit_search=strpbrk(str,digits);//检查str中是否首次出现数字
    while(digital_search!=NULL){//while数字不以str结尾
    数字金额++;
    数字搜索=strpbrk(数字搜索+1,数字);
    }
    位数频率=(双)位数金额/(双)总金额*100.0;
    串前串;
    pre_str=“总计:”+至字符串(总计)+“\n”
    元音:“+到字符串(元音数量)+”\n”
    频率:“+到字符串(元音频率)+”\n”
    “数字:“+到字符串(数字金额)+”\n”
    “频率:”+到字符串(数字频率)+“\n”;
    
    cout A需要最小化。输入的处理似乎不是问题的一部分,只需将其删除即可。如果执行
    if(ReadFile(pipe_实例[i],buf,1024,&read_字节,NULL))
    每个客户端的循环中..已经可以说所有设计都是错误的。必须处理每个连接independed@RbMm,绝对没有办法只更改部分代码?必须重写所有内容?我确定需要重写所有内容。但这只是我的意见。嗨,答案解决了你的问题吗?请随时告诉我如果你有任何问题,也接受它,如果它确实有帮助。
    Client is launched
    Pipe connection established
    Enter string (max 1023 symbols): 12345qwerty
    ***BREAKPOINT 1: no answer from server, no debug output in server side (waiting state)***
    (waiting)
    ***BREAKPOINT 2: immediate response from server and debug output from server(firstly about
    first client(wrote for the second time), and then about second client(wrote for the first 
    time)). All clients get answer from server***
    Writting and reading was successful
    Total: 11
    Vowels: 2
    Frequency: 18.181818
    Digits: 5
    Frequency:45.454545
    Press any key to continue . . .
    (press)
    (termination)
    
    Client is launched
    Pipe connection established
    Enter string (max 1023 symbols): qwiouryq18927468
    ***BREAKPOINT 2: immediate response from server and debug output from server(firstly about 
    first client(wrote for the second time), and then about second client(wrote for the first 
    time)). All clients get answer from server***
    Writting and reading was successful
    Total: 16
    Vowels: 4
    Frequency: 25.000000
    Digits: 8
    Frequency:50.000000
    Press any key to continue . . .
    (press)
    (termination)
    
    #include <windows.h> 
    #include <iostream>
    #include <tchar.h>
    #include <strsafe.h>
    #include <limits>
    #include <string.h>
    #include <sstream>
    #include <thread>
    
    #pragma warning(disable : 4996)
    
    using namespace std;
    
    #ifdef max
    #undef max
    #endif
    
    
    #define PIPE_TIMEOUT 5000
    #define BUFSIZE 4096
    HANDLE* pipe_instances = NULL;
    int n = 0;
    
    
    int f(int i)
    {
        char buf[1024];
        DWORD read_bytes;
        DWORD written_bytes;
        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;
        }
    }
    
    
    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 occurence of vowels and numbers in the entered string. Further,\n"
        //        "this information is sent to the client.\n";
        //    return EXIT_SUCCESS;
        //}
    
        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("D:\\VS_test_projects\\Project2\\Debug\\c.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***
    
        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***
    
        thread* t = new thread[n];
        for (int i = 0; i < n; i++)
        {
            t[i] = thread(f, i);
        }
    
    
        cout << "Reading, processing and writting was successful\n";
    
        // ***CLOSING PIPE INSTANCES***
        for (int i = 0; i < n; i++)
        {
            t[i].join();
        }
        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;
    }