У меня есть функция для чтения TCP-пакетов неизвестного размера. Функция использует MSG_PEEK, чтобы найти необходимый размер полученного сообщения JSON (символ \"} в условии цикла), а затем запускает цикл еще раз без MSG_PEEK для очистки буфера. По какой-то причине два вывода в конце функции работают отлично, но когда я вызываю fn, моя программа сразу после возврата умирает и печатает «*** обнаружено разрушение стека ***: прекращено». content_buffer не должно переполняться, потому что я выделяю content_size + 1 и передаю content_size в recv, верно?
char *read_socket_raw(int socket)
{
if (socket == -1)
{
fprintf(stderr, "Reading socket failed.\n");
return NULL;
}
char *content_buffer = NULL;
unsigned short content_size;
why2_bool empty_buffer = 0; //WHETHER MSG_PEEK SHOULD BE USER OR NOT
do
{
//FIND THE SENT SIZE
content_size = 0;
if (ioctl(socket, FIONREAD, &content_size) < 0 || content_size <= 0) continue;
//ALLOCATE
content_buffer = realloc(content_buffer, content_size + 1);
read_section:
//READ JSON MESSAGE
if (recv(socket, content_buffer, content_size, !empty_buffer ? MSG_PEEK : 0) != content_size) //READ THE MESSAGE BY CHARACTERS
{
fprintf(stderr, "Socket probably read wrongly!\n");
}
if (empty_buffer) goto return_section; //STOP LOOPING
} while (content_buffer == NULL || strncmp(content_buffer + (content_size - 2), "\"}", 2) != 0);
//REMOVE JUNK FROM BUFFER (CUZ THE MSG_PEEK FLAG)
empty_buffer = 1;
goto read_section;
return_section:
content_buffer[content_size] = '\0';
printf("'%s'\n", content_buffer);
printf("'%p'\n\n", content_buffer);
return content_buffer;
}
Если вызов ioctl завершится неудачно или content_size <= 0, вы выйдете из цикла и повторно войдете в него (с помощью goto read_section;), не выделяя буфер. бум
Извините, вы также можете передать стандартное перераспределение. Моя версия realloc использует только мой сборщик мусора. :)
«вы также можете передать стандартный realloc» — лучше, если вы предоставите минимальный воспроизводимый пример. Если каждый, кто читает вопрос, должен будет это сделать, будет много разных версий программы.
Также см. мой второй комментарий, в котором указан один из возможных способов сбоя.
Вам не обязательно знать, сколько байт доступно, прежде чем читать. Особенно если вы собираетесь продолжать чтение до тех пор, пока не получите все нужные данные — с таким же успехом вы можете просто выполнить блокирующее чтение. Обратите внимание, что при таком чтении может быть передано меньше байтов, чем запрошено, и, как правило, это будет сделано, если можно передать хотя бы один байт без блокировки, но не полное запрошенное число.
Однако учтите, что ваш подход хрупок. TCP не имеет встроенных границ сообщений, поэтому у вас, вероятно, возникнут проблемы, если сообщения JSON будут получены достаточно близко друг к другу, и вы не очистите одно из них до того, как начало следующего попадет в очередь приема.
Я не против периодического использования goto для пояснения, но использование goto для перехода в цикл извне — это извращение. Скорее запутывает, чем уточняет.
TCP-пакетов не существует, TCP — это поток. Даже если отправитель отправляет TCP по частям, они могут быть доставлены как одна часть или разделены в любом месте. Поэтому используйте буфер и читайте до sizeof(buffer) байт. Результат сообщает, сколько байт вы получили или произошла ли ошибка. Функции select() и poll() можно использовать для проверки наличия ожидающих данных.





В вашей функции есть что не нравится, но единственное, что кажется совершенно неправильным, — это тип переменной content_size. FIONREAD ioctl ожидает указатель на int, но вместо этого вы предоставили указатель на unsigned short. Неопределенные результаты поведения.
На практике ваш unsigned short, скорее всего, меньше int, и в этом случае ioctl() предположительно попытается записать за пределы этого объекта. Это как раз тот случай, который может вызвать сообщение о разрушении стека и сбой, поскольку, если хранилище для этой переменной находится в начале (верху) кадра стека функции, то выход за его границы действительно приведет к разрушению стека.
Другие вещи, которые не нравятся:
необоснованное использование goto. Я не фанатик goto, но на практике структурированные конструкции управления потоком почти всегда предпочтительнее. И я никогда не сталкивался с использованием goto, с которым я бы согласился, что (i) прыгнул назад или (ii) прыгнул в цикл или условное выражение извне.
вы создаете себе ненужные проблемы и сложности, пытаясь определить длину сообщения перед его прочтением. Вам не нужно этого делать. Просто выделите буфер, достаточно большой для большинства сообщений, и читайте в него все, что сможете. Перераспределите и прочтите еще немного, если вы заполните буфер чтения, не получив полного сообщения. Это не будет проблемой, если входящее сообщение будет меньше буфера — вы просто получите короткое чтение.
вам, вероятно, нужно быть более изощренным в распознавании конца сообщения. Если в текущем коде вам не удастся очистить все байты одного сообщения из буфера приема до того, как туда попадет часть или все другое сообщение, вы не сможете правильно распознать конец первого.
Огромное спасибо, чувак, изменение типа данных на int исправило это. Я постараюсь исправить переход, мне он тоже не нравится, если честно. Кроме того, у меня уже есть код, проверяющий конец сообщения вне функции read_socket_raw. Что касается длины, я просто думаю, что это самое элегантное решение, потому что у меня нет установленного ограничения на длину сообщения, и я не хочу этого делать. Отмечаем как решенную. Спасибо.
Что такое
why2_realloc? Покажите, пожалуйста, минимально воспроизводимый пример . Примечание: использование вамиgotoзатрудняет выполнение программы. Старайтесь избегать прыжков в циклы снаружи и т. д.