Разбор одного сообщения, поступающего через сокет, содержащий 2 строки в C - PullRequest
0 голосов
/ 07 июня 2018

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

Обратите внимание, что я использую C в среде Ubuntu.

Формат сообщения в одном void* buffer:

[string1]\0[string2]\0

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

Я собираюсь использовать пример, когда из буфера читается только 1 строка, но я упоминаю стратегию, потому что я не могу просто поместить сообщение в char* buffer.Мне нужна функция для извлечения строки из более сложных буферов.

Все начинается так:

void* buffer = malloc(msgSize * sizeof(char)); //the message size is properly calculated to include the '\0' at the end
char* instanceId = malloc(msgSize * sizeof(char));

if(recv(socket_desc, (void*) buffer, msgSize * sizeof(char), MSG_WAITALL) <= 0) {
        log_error(logger, "Message failed.");
        return;
    }

    bufferToString(buffer, &instanceId, 0);
    bufferToString2(buffer, instanceId, 0);

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

Попытка #Number 1: символ за символом

int bufferToString(void* buffer, char** string, int startPtr) {
    //startPtr can be used to read strings that are in the middle of a buffer
    char a;
    int thisStringPtr = 0; 

    do {
        a = *(char*) (buffer + startPtr);
        (*string)[thisStringPtr] = a;
        startPtr++;
        thisStringPtr++;
    } while (a != '\0');
    return startPtr; //return end position to use for extracting more values later

}

Этот жалуется:

==23047== Invalid read of size 1
==23047==    at 0x403A27A: bufferToString (buffer.c:16)
==23047==    by 0x804A0C2: handleHiloInstancia (coordinador.c:232)
==23047==    by 0x8049C54: procesarConexion (coordinador.c:85)
==23047==    by 0x4066294: start_thread (pthread_create.c:333)
==23047==    by 0x41650AD: clone (clone.S:114)
==23047==  Address 0x423bc8a is 0 bytes after a block of size 10 alloc'd
==23047==    at 0x402C17C: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==23047==    by 0x804A063: handleHiloInstancia (coordinador.c:225)
==23047==    by 0x8049C54: procesarConexion (coordinador.c:85)
==23047==    by 0x4066294: start_thread (pthread_create.c:333)
==23047==    by 0x41650AD: clone (clone.S:114)

Строка16 из bufferToString - это первая строка внутри оператора do.

Попытка 2: приведение и копирование

int bufferToString2(void* buffer, char* string, int startPtr) {
    strcpy(string, (char*) (buffer + startPtr));
    return (strlen(string) + 1)*sizeof(char);
}

С или без + startPtr, это вызывает несколько иные проблемы:

==23190== Invalid read of size 1
==23190==    at 0x402F489: strcpy (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==23190==    by 0x403A1E3: bufferToString2 (buffer.c:3)
==23190==    by 0x804A0C1: handleHiloInstancia (coordinador.c:232)
==23190==    by 0x8049C54: procesarConexion (coordinador.c:85)
==23190==    by 0x4066294: start_thread (pthread_create.c:333)
==23190==    by 0x41650AD: clone (clone.S:114)
==23190==  Address 0x423bc8a is 0 bytes after a block of size 10 alloc'd

Iпопробовал несколько других комбинаций (например, использование строки char ** и все необходимые изменения в bufferToString2), но я продолжаю получать похожие сообщения об ошибках.Что я не вижу?

ОБНОВЛЕНИЕ: Как отправляется сообщение:

    int bufferSize;
    void* buffer = serializePackage(HANDSHAKE_INSTANCE_ID ,instancia_config->nombre, &bufferSize );
    printf("Buffer size: %i - Instancia Name = %s - Socket num: %i\n", bufferSize, instancia_config->nombre, socket_coordinador); //this shows right data
    if (send(socket_coordinador,buffer,bufferSize, 0) <= 0) {
        log_error(logger, "Could not send ID.");
        endProcess(EXIT_FAILURE);
    }

instancia_config-> nombre имеет тип char *

void* serializePackage(int codigo,char * mensaje, int* tamanioPaquete){

    int puntero = 0;
    int length = strlen(mensaje);

    int sizeOfPaquete = strlen(mensaje) * sizeof(char) + 1 + 2 * sizeof(int);
    void * paquete = malloc(sizeOfPaquete);


    memcpy((paquete + puntero) ,&codigo,sizeof(int));
    puntero += sizeof(int);

    memcpy((paquete + puntero),&length,sizeof(int));
    puntero += sizeof(int);

    memcpy((paquete + puntero),mensaje,length * sizeof(char) + 1);

    *tamanioPaquete = sizeOfPaquete;
    return paquete;

}

Ответы [ 2 ]

0 голосов
/ 08 июня 2018

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

К сожалению, отсутствует много информации для серьезного рассмотрения этого вопроса.

Например, какой тип сокета вы используете для отправки / получения ваших сообщений?Это труба или сетевой сокет - какой вид транспорта вы тогда используете?

Как вы гарантируете, что получили все сообщение?Вы должны как минимум проверить возвращаемое значение recv (которое является числом полученных октетов) и проверить с ожидаемой длиной, если таковой есть.

Получив ваше сообщение, сбросьте сообщениекак есть - если вы не знакомы с использованием отладчика, функция, подобная этой, будет работать как стартер:

void dump(const char* buffer, size_t length) {
    for(size_t i = 0; i < length; ++i) {
        printf("%x", buffer[i] & 0xff);
    }
    printf("\n");
}

и вызывать ее в вашем коде после ssize_t received = recv(...), как dump(buffer, received).

Кроме того, вы не предоставляете способ вычисления msgSize в своем первом фрагменте кода - как вы гарантируете, что строка в mensaje, которую вы передаете serializePacket, не длиннее msgSize?

Затем, в serializePacket, вы создаете буфер, заполненный так:

| codigo (int) | length (int) | mensaje ( = terminal zero) |

, но то, что вы читаете на другом конце, это просто строка ac -вам также следует прочитать 2 ints, не так ли?

Тогда есть еще одна проблема с этим битом сериализации: даже если вы прочитаете 2 ints, у вас будет фрагмент кода, которыйсовершенно непереносимо и работает только если би отправитель, и получатель работают на архитектуре, которая представляет int точно так же и в том же порядке битов.если вы запускаете отправителя в 32-битной системе, а получателя - в 64-битной системе, вы записываете 4-байтовые целые числа при попытке прочитать 8-байтовые целые числа.Лучше использовать типы с точно определенной шириной (например, uint32_t из inttypes.h) и явно преобразовывать в / из сетевого порядка байтов, используя, например, ntohl(3) / htonl(3).

Наконец, я хочу добавить несколько замечаний для улучшения качества кода, которые могут предотвратить множество потенциальных ошибок:

Возьмите в качестве примера ваш bufferToString:

  1. Вы передали string как char** - почему?
  2. Используйте соответствующие типы данных - не используйте подпись int, когда вы имеете дело с неотрицательными размерами - используйте стандартный тип size_tвместо этого
  3. Не используйте void* в качестве типа, если это действительно не нужно - вы теряете чертовски много ошибок проверки времени компиляции.C автоматически конвертируется из void* в присваиваниях.Поэтому настоятельно рекомендуется объявить buffer как char*.

Эта функция, например, может быть резко упрощена:

size_t bufferToString(const char* buffer, char* string, size_t offset) {
    for(size_t i = offset; 0 != buffer[i]; ++i) {
        string[i] = buffer[i];
    }
    return ++i;
}

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

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

0 голосов
/ 07 июня 2018

У вас есть свой SRC и DST правильный путь?Назначение (или цель, если вы предпочитаете) сервисов памяти в C является первым параметром, поэтому: strcpy (строка, буфер) скопирует буфер в строку.(https://www.tutorialspoint.com/c_standard_library/c_function_strcpy.htm)

Но : bufferToString2 вызывается с буфером в качестве первого параметра (и это источник в этом случае).

В первом случае, какбыло указано, что вы не можете делать арифметику для пустоты *, потому что математика пытается перейти к N-му элементу, если вы скажете: * (x + N), и если x 'пусто', у нее нет размера, и поэтомуN-й элемент не имеет смысла.

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