Я вижу неожиданный ввод в моем буфере после чтения сокета - PullRequest
0 голосов
/ 10 июля 2020

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

Это моя отправка и функции чтения с веб-сервера:

    int total_to_send = request.size();
    int sent;
    int total_sent = 0;
    while(total_sent < total_to_send) {
        sent = send(_socket, &request.data()[total_sent], total_to_send - total_sent, 0);
        if(sent <= 0) {
            break;
        }
        total_sent += sent;
    }
    if(sent <= 0) {
        printf("[Thread #%i] Error occurred when sending request... (%i/%i, %i @ %s)\n", total_sent, total_to_send, sent, path.c_str());
        break;
    }

    int read;
    int total_read = 0;
    while(true) {
        read = recv(_socket, &buffer[total_read], sizeof(char) * 1024, 0);
        total_read += read;
        if(read <= 0)
            break;
        if(total_read > 0 && buffer[total_read - 1] == '}')
            break;
    }
    if(read < 0 || total_read == 0) {
        printf("[Thread #%i] Error occurred when reading response... (%i, %i @ %s)\n", ThreadID, read, total_read, path.c_str());
        break;
    }

Буфер - это char buffer[1024000];. Я почти уверен, что в буфере достаточно места для ответа.

Я пытаюсь проверить правильность начала и окончания ответа следующим образом:

    if(buffer[total_read - 1] != '}') {
        printf("[Thread #%i] Invalid response from the server... ('%c', %i, %i @ %s)\n", ThreadID, buffer[total_read - 1], read, total_read, path.c_str());
        break;
    }

    int start_pos = -1;
    for(int i = 0; i < total_read; ++i) {
        if(buffer[i] == '{') {
            start_pos = i;
            break;
        }
    }

    if(start_pos == -1) {
        printf("[Thread #%i] Invalid response from the server... (%i, %i @ %s)\n", ThreadID, read, total_read, path.c_str());
        break;
    }

    if(step == 0) {
        static const char NonExistingResource[11] = "Not found.";
        if((total_read - start_pos) == 10) {
            int s = 0;
            for(int c = 0; c < 11; ++c) {
                if(NonExistingResource[c] == buffer[start_pos + c]) {
                    ++s;
                }
            }
            if(s == 10) {
                printf("[Thread #%i] The resource %i does not exist...\n", ThreadID, StartID);
                step = 9;
                break;
            }
        }
    }

Все работает, как ожидалось - ну, по крайней мере, в большинстве случаев это так. Я добавляю в буфер немного больше информации, а затем пытаюсь ее записать:

    buffer[total_read] = '\n';
    buffer[start_pos - 1] = '#';

    fwrite(&buffer[start_pos - 1], sizeof(char), (total_read - start_pos) + 2, file);

Проблема начинается позже. В 99% случаев я записываю в файл действительный json (например, #{"id":3,"numbers":[1, 3, 7],"time":1323423610}), но в оставшемся 1% мой код записывает все прочитанное содержимое. И это содержимое следует за предыдущей строкой без добавления каких-либо разрывов строки или символа #. Например,

#{"id":1,"numbers":[1, 3, 7],"time":1323423610}
#{"id":2,"numbers":[2, 4, 8],"time":1323423610}
#{"id":3,"numbers":[9],"time":1323423610}HTTP/1.1 200 OK
Content-Type: application/json;charset=utf-8
Content-Length: 88
Connection: keep-alive
Date: Fri, 10 Jul 2020 07:53:42 GMT
Server: openresty
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, HEAD, POST, PUT, DELETE, OPTIONS
Access-Control-Expose-Headers: ETag, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, Retry-After, Content-Length, Content-Range
Cache-Control: public, max-age=21600
x-memc: MISS, STORE
x-memc-key: 8c68004d1219af73acc5312e3fdf554e
x-memc-age: 0
x-memc-expires: 21600
Last-Modified: 2020-04-11 11:27:09 +0000
X-Cache: Hit from cloudfront
Age: 4478

{"id":4,"numbers":[5],"time":1323423610}
#{"id":5,"numbers":[6],"time":1323423610}

Теперь мой код явно находится на al oop. Он отправляет несколько запросов, используя один и тот же сокет, и воссоздает сокет, когда сервер возвращает ошибку или запросы терпят неудачу. Кроме этого, я никогда не пытаюсь изменить буфер, за исключением того, что иногда сбрасываю его, но это не должно вызывать никаких проблем, потому что я не редактирую его между чтением и fwrite.

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

Между прочим, код предназначен для одновременной работы с несколькими потоками, но каждый поток имеет свой собственный сокет и ФАЙЛ *, поэтому никакой другой поток не мешает процессу записи . Иногда некоторые потоки делают это, а некоторые - нет, хотя я говорю, что нужно отправлять одинаковое количество запросов.

Любая помощь / идеи приветствуются людьми!

1 Ответ

1 голос
/ 10 июля 2020

Не уверен, что это проблема, но если сокет возвращает ошибку (-1), вы добавляете -1 к total_read, а затем выходите из l oop.

Это:

while(true) {
    read = recv(_socket, &buffer[total_read], sizeof(char) * 1024, 0);
    total_read += read;
    if(read <= 0)
        break;
    if(total_read > 0 && buffer[total_read - 1] == '}')
        break;
}

Лучше:

while(true) {
    read = recv(_socket, &buffer[total_read], sizeof(char) * 1024, 0);
    if(read <= 0)
        break;
    total_read += read;
    if(total_read > 0 && buffer[total_read - 1] == '}')
        break;
    buffer[total_read] = '\0'; // null terminate, makes debugging easier
}

Кроме того, похоже, что вы действительно делаете HTTP-запрос, но делаете что-то чрезвычайно взломанное, чтобы обнаружить начало / конец содержимого ответа тело. (И сделать что-то еще более хитрое, чтобы обнаружить ответ 404 Not Found). Любой файл git HTTP-клиент будет анализировать MIME строки заголовков, находить заголовок Content-Length и ссылаться на это значение, чтобы узнать, сколько байтов следует читать после пустой строки. Рассматривали ли вы использование реальной HTTP-библиотеки, такой как Boost Beast или libCurl?

В противном случае, я подозреваю, у вас есть комбинация ошибки сокета и неинициализированной переменной, которая заставляет вас читать из случайного места в буфере.

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