Пара сокетов C ++ не читает / не пишет Родитель / Ребенок - PullRequest
0 голосов
/ 15 октября 2018

Мне был назначен проект, в котором мне нужно использовать Unix Domain Sockets для двусторонней связи между родительским и дочерним процессами.Мой первоначальный подход заключался в создании дочернего элемента и сервера, но у меня возникали серьезные проблемы с подключением и путями.Если кто-то предпочел бы увидеть этот код, я готов предоставить его.

В любом случае, я достигаю строки в моей программе, где я печатаю «родитель написал», хотя я не совсемубежден, что это потому, что не происходит последующее чтение.Я работал с закрытием каналов в разных местах и ​​изменением того, как я читаю / пишу.

Теперь я использую socketpair () для настройки пары сокетов.Я не могу использовать любой другой тип IPC, кроме сокетов.Эта информация совершенно новая для меня, так что простите небрежный код.

Пожалуйста, игнорируйте длинный список #include, очистите как можно скорее.

Опять же, мой текущий вывод останавливается на "Parent write to child", а затем он продолжает работать, не закрывается, поэтому ядумаю, что это ребенок, ожидающий что-то получить.

Моя программа должна иметь возможность читать и записывать невероятно длинные файлы, поэтому вы увидите работу по сериализации и анализу по всему коду.Сериализация и синтаксический анализ отлично работают в другой части проекта, поэтому я знаю, что это не проблема.Это чертовски сокеты!

В любом случае, код:

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string>
#include <iostream>
#include <algorithm>
#include <sys/wait.h>
#include <vector>
#include <fstream>

int main() {
    int sockets[2];

    std::cout << "Please enter name of text file." << std::endl;
    std::string entered_file;
    std::cin >> entered_file;
    std::string string_to_find;
    std::cout << "Please enter the string you'd like to search for." << std::endl;
    std::cin >> string_to_find;

    int rc = socketpair(AF_UNIX, SOCK_STREAM, 0, sockets);
    if (rc < 0) {
        perror("socketpair");
        exit(1);
    }

    pid_t p;
    p = fork();

    if (p == 0)  //child
    {
        char ch;
        std::string the_file_as_string = "";
        std::vector<std::string> lines_with_string;

        int r;
        while ((r = read(sockets[0], &ch, 1)) > 0) {
            the_file_as_string.push_back(ch);
        }

        std::cout << "Child has read from parent..." << std::endl;

        size_t pos = 0;
        while(the_file_as_string.find(string_to_find, pos) != std::string::npos) {
            pos = the_file_as_string.find(string_to_find, pos+string_to_find.size());
            std::string sub = the_file_as_string.substr(0,pos);
            int occurrences = 0;
            size_t pos2 = 0;
            while (sub.find("/0", pos2 ) != std::string::npos) {
                pos2 = sub.find("/0", pos2+2);
                ++ occurrences;
                pos += string_to_find.length();
            }
            occurrences = occurrences;
            std::string occurrences_string = std::to_string(occurrences);
            lines_with_string.push_back(occurrences_string);
        }
        std::cout << "child has parsed" << std::endl;
        std::string lines_with_string_as_string = "";
        for(unsigned int i=0; i<lines_with_string.size(); i++) {
             lines_with_string_as_string = lines_with_string_as_string + lines_with_string.at(i) + "/0";
         }
        int file_size = lines_with_string_as_string.size();

        write(sockets[0], lines_with_string_as_string.c_str(), file_size+1);
        close(sockets[0]);
        std::cout << "child has sent back to parent..." << std::endl;
    }
    else  //parent
    {
        close(sockets[0]);
        std::ifstream myfile;

        //open file
        myfile.open(entered_file.c_str());

        if (!myfile) {
            std::cerr << "Unable to open file datafile.txt" << std::endl;
            return 1;   // call system to stop
        }


        //here, pass lines to child one by one and return true or false based on if it finds the string
        char lines_with_string[100];
        char ch;
        std::vector<std::string> the_file;
        std::vector<int> final_line_numbers;
        std::vector<std::string> final_lines;
        std::string str;
         while (std::getline(myfile, str))
         {
             the_file.push_back(str);
         }
         std::string the_file_as_string = "";

         for(unsigned int i=0; i<the_file.size(); i++) {
             the_file_as_string = the_file_as_string + the_file.at(i) + "/0";
         }

         int big_size = the_file_as_string.size();
         write(sockets[1], the_file_as_string.c_str(), big_size+1);

         std::cout << "Parent wrote to the child..." << std::endl;

         wait(NULL);

        std::string lines_with_string_as_string = "";
        int r;
        while ((r = read(sockets[1], &ch, 1)) > 0) {
            lines_with_string_as_string.push_back(ch);
        }
        close(sockets[1]);
        std::cout << "Parent has read from child..." << std::endl;

        std::string delimit = "/0";
        size_t pos3 = 0;
        while ((pos3 = lines_with_string_as_string.find(delimit)) != std::string::npos) {
            std::string token = lines_with_string_as_string.substr(0, pos3);
            int token2 = std::stoi(token);
            final_line_numbers.push_back(token2);
            lines_with_string_as_string.erase(0, pos3 + delimit.length());
        }
        //match line numbers to array of lines (original)
        for(unsigned int i=0; i < final_line_numbers.size(); i++) {
            int find_int = final_line_numbers.at(i);
            std::string find_string = the_file.at(find_int-1);
            std::cout << find_string << std::endl;
            final_lines.push_back(find_string);
        }

        std::sort(final_lines.begin(), final_lines.end());
        std::cout << "The final outcome with lines containing " << "'" << string_to_find << "' are:" << std::endl;
        for(unsigned int i=0; i<final_lines.size(); i++) {
            std::cout << final_lines.at(i) << std::endl;
        }
         myfile.close();
         std::cout << "Program has exited completely." << std::endl;
    }
}

МИНИ-ВЕРСИЯ (та же проблема, говорит «Родитель написал ребенку ...», а затем ничего не делает,а также не выходит:

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string>
#include <iostream>
#include <algorithm>
#include <sys/wait.h>
#include <vector>
#include <fstream>

int main() {

    int sockets[2];

    pid_t p;


    socketpair(AF_UNIX, SOCK_STREAM, 0, sockets);

    p=fork();

    if (p < 0) {
        perror("forking");
    }
    else if(p > 0) {                                                //parent
        std::string long_string_to_send = "ladjfldjsfljasdfj adjlkjadlsjf a fljasdfladj ljdl aljfajdfljadfadsflajd  ajfdlkjslfjadj faldjsljalsdfljdfljadsl jladsfjasflajdflkajfl dasl fjalfjldjsfladsflajdlsfald fjljdfljadfjadfjl djfljadlfj aldjl hello";
        int big_size = long_string_to_send.size();

        write(sockets[1], long_string_to_send.c_str(), big_size+1);
        std::cout << "Parent has written to child..." << std::endl;

        wait(NULL);

        std::string lines_with_string_as_string = "";
        int r;
        char ch;
        while ((read(sockets[1], &ch, 1)) > 0) {
            lines_with_string_as_string.push_back(ch);
        }
        close(sockets[1]);
        std::cout << "Parent has received from child..." << std::endl;
        std::cout << "Parent has read the following... " << lines_with_string_as_string << std::endl;
        std::cout << "Program has exited completely" << std::endl;
    }
    else {                                                          //child
        char ch;
        std::string the_file_as_string = "";

        int r;
        while ((read(sockets[0], &ch, 1)) > 0) {
            the_file_as_string.push_back(ch);
        }

        std::cout << "Child has read from parent..." << std::endl;
        std::cout << "Child has read the following... " << the_file_as_string << std::endl;
        int file_size = the_file_as_string.size();

        write(sockets[0], the_file_as_string.c_str(), file_size+1);
        close(sockets[0]);
    }
}

1 Ответ

0 голосов
/ 15 октября 2018

Сгоревший ... ваш родитель делает:

write(sockets[1], ...
wait(NULL)

Пока ребенок делает:

while (read(sockets[0] ....)   > 0)
...

Ключ к этой проблеме: почему read из sockets[0]когда-нибудь вернуть что-то меньше или равно нулю.Ответ здесь: никогда.Вы, вероятно, намереваетесь, чтобы ребенок прочитал все, что родитель записал в сокет, но работа сокетов заключается в том, что вы не получаете указание конца файла, пока другой узел не закрыл сокет (или shutdown(2).

Так что в родительском вы должны сделать что-то вроде:

write(sockets[1], ....);
shutdown(sockets[1], SHUT_WR);             <<<<<=================
wait(NULL)

Вызвав shutdown с SHUT_WR на конце родителя, дочернийполучит указание EOF (возвращаемое значение ноль) на его конце сокета. В противном случае чтение дочернего элемента будет заблокировано навсегда (потому что, как ОС узнает, если родительский объект запишет больше данных позже?).

Небольшое дальнейшее объяснение: родитель также может close конец своего сокета, но если он это сделает, он не сможет прочитать данные ответа от дочернего элемента. shutdown по существу позволяет вам«полузакрыть» сокет, то есть закрыть для записи, но оставить открытым для чтения.

Единственная реальная альтернатива этому подходу - это каким-то образом «кадрировать» данные (т.е. создавать «протокол»"). То естьВы можете сначала записать длину данных, которые вы будете отправлять, в каком-то фиксированном размере или каким-либо другим способом, и потом ребенок может прочитать именно то количество необходимых байтов.Затем вы можете бесконечно продолжать диалог между двумя процессами без необходимости shutdown или close.

РЕДАКТИРОВАТЬ:
Еще одно предупреждение.Для каждого процесса рекомендуется после fork закрывать другую сторону сокета.Другими словами, сразу после разветвления родитель закроет sockets[0], а ребенок закроет sockets[1].В противном случае close с одной стороны не фактически создаст конец файла на другой стороне (поскольку дескриптор файла все еще открыт в стороне чтения - сразу после того, как fork обафайловые дескрипторы открыты в обоих процессах).Использование shutdown / SHUT_WR позволяет обойти эту трудность, поскольку явно указывает, что одна сторона должна считаться закрытой для целей написания.

...