C ++: тип, созданный вне "try", вызывает ошибку, но внутри это не так - PullRequest
2 голосов
/ 12 октября 2019

Это - с чисто точки зрения решения проблем - решаемая проблема, но базовый механизм может быть интересен некоторым, включая меня.

Я написал довольно простой сервер, прослушивающий данный порт TCP,Я написал это таким образом, чтобы он мог принимать несколько запросов, а не только один, и завершать работу с циклом while.

Поскольку у меня нет опыта работы с C ++, я нашел решение в Google и сфабриковал егомои потребности. Часть кода, о которой идет речь, выглядит следующим образом:

    socklen_t addrlen = sizeof(address);

    while(true) {
        try {
            std::cerr << "Listening..." << std::endl;
            if(listen(socketId, 3) < 0) {
                std::cerr << "Error while listening for incoming connections" << std::endl;
                throw;
            }
            processRequest(accept(socketId, (struct sockaddr *) &address, &addrlen));
        } catch(Json::RuntimeError& e) {
            std::cerr << "Json error: " << e.what() << std::endl;
        } catch(...) {
            std::cerr << "Unknown error" << std::endl;
            throw;
        }
    }

Проблема с этим кодом сложна и может быть очевидна для более опытных из вас, но она определенно не очевидна для меня.

Я покажу вам часть определения класса:

    int socketId;
    sockaddr_in address;
    const RequestProcessor& processor;

Обратите внимание на порядок этих переменных.

Используя метод выше и переменные в этом порядке, первыйзапрос выполняется гладко, и программное обеспечение начинает прослушивать следующий запрос, как и предполагалось.

Однако, когда приходит второй запрос, ссылка processor перезаписывается, предположительно, из-за некоторого переполнения памяти - или чего-то другогоnamed - происходящее, которое перезаписывает ячейку памяти, на которую указывает эталон processor, вызывая ошибку где-то вниз по линии в методе, не показанном здесь.

Решение простое, но найти его действительно сложно. :

    while(true) {
        try {
            std::cerr << "Listening..." << std::endl;
            if(listen(socketId, 3) < 0) {
                std::cerr << "Error while listening for incoming connections" << std::endl;
                throw;
            }
            socklen_t addrlen = sizeof(address);
            processRequest(accept(socketId, (struct sockaddr *) &address, &addrlen));
        } catch(Json::RuntimeError& e) {
            std::cerr << "Json error: " << e.what() << std::endl;
        } catch(...) {
            std::cerr << "Unknown error" << std::endl;
            throw;
        }
    }

Все, что я сделал, это заменил место создания переменной addrlen. Это решает проблему и дает дополнительное преимущество, так как является более «разумным».

Вопрос в том, почему перемещение инициализации addrlen решает эту проблему?

1 Ответ

3 голосов
/ 13 октября 2019

accept использует третий аргумент (т. Е. addrlen) в качестве входа и выхода. На входе должен быть размер структуры, на которую указывает второй аргумент (т. Е. address), а на выходе будет фактический размер адреса.

Адрес может быть усечен, если он не подходитструктура, основанная на вводе третьего аргумента.

Если accept возвращает большее значение для addrlen, чем sizeof(address), то во второй итерации аргумент, переданный в accept, не будет соответствовать sizeof(address) больше и неопределенное поведение приведет. На практике accept запишет адрес после address в processor, если члены класса расположены в таком порядке.

Установка addrlen в sizeof(address) в каждой итерации цикла непосредственно перед вызовомaccept является правильным решением и гарантирует, что это значение всегда используется в качестве ввода для accept, даже если accept изменяет его значение на выходе.

Также убедитесь, что вы используете соответствующиеsockaddr_* тип для семейства протоколов, которое вы используете для сокета прослушивания, например sockaddr_in для AF_INET (ipv4) или sockaddr_in6 для AF_INET6 (ipv6).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...